diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/404.html b/404.html new file mode 100644 index 000000000..55b23821b --- /dev/null +++ b/404.html @@ -0,0 +1 @@ + MySQL WIKI

404 - Not found

\ No newline at end of file diff --git a/CNAME b/CNAME new file mode 100644 index 000000000..19f826653 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +mysql.fengzhao.me \ No newline at end of file diff --git a/SQLite/SQLite/index.html b/SQLite/SQLite/index.html new file mode 100644 index 000000000..54d29541d --- /dev/null +++ b/SQLite/SQLite/index.html @@ -0,0 +1 @@ + 概述 - MySQL WIKI
跳转至

概述

SQLite 现在已经是全球用户最多的数据库产品。它非常小巧以及单文件无单独操作系统进程,就像病毒一样依附在宿主程序的进程里运行。你看不到它,但它却无处不在。

汽车,手机,浏览器,以及各类 app 里都能见到 .db 结尾的 SQLite 数据库文件。 假如 SQLite 出现重大 bug,或者像平常的数据库那样无法连接,整个地球都会乱套。你身边用的几乎所有电子产品(手机,电脑,iPad,笔记本)和嵌入式设备全部都会出问题。它的诞生到大范围全球流行的过程和一般软件有着不太一样的发展历程。

SQLite 诞生的契机就是典型的程序员开发的故事剧本。作者 Richard 最开始在一艘军舰上做 contractor(就是我们说的外包)。他们程序跑在军舰安装的电脑上,电脑上装的是 informix。Richard 的工作就是把 informix 的数据拿出来进行计算然后展示到电脑屏幕上(这和我们今天的 CRUD 工作类似)。比较令人恼火的是 informix 很不稳定,经常崩溃连不上。部队里的铁拳长官可不懂啥 TCP/IP 或者数据库系统知识。他们只看到软件的报错 dialog(对话框) 经常弹出来,而这个 dialog 又是 SQLite 的作者 Richard 写的软件画出来的,锅自然从天而降。于是 Richard 决定自己从头写一个无需外部连接的数据库来解决这个问题。

SQLite 是一个 C 实现的 SQL 数据库引擎,它的特点是小型、快速、自包含、高可靠性和功能齐全。SQLite 嵌入在所有手机和大多数计算机中,也捆绑在为数众多的其它应用中,是世界上使用量最大的数据库引擎。SQLite是一个进程内的轻量级嵌入式数据库,它的数据库就是一个文件,实现了自给自足、无服务器、零配置的、事务性的SQL数据库引擎。

它是一个零配置的数据库,这就体现出来SQLite与其他数据库的最大的区别:SQLite不需要在系统中配置,直接可以使用。且SQLite不是一个独立的进程,可以按应用程序需求进行静态或动态连接。SQLite可直接访问其存储文件。

SQLite 经过 20 多年发展,支持几乎所有数据库功能,并且能跑在几乎所有小型的嵌入式环境里。对这些小型环境需要做的优化比普通数据库要更多,实现起来要更复杂。想学习数据库的实现从 SQLite 入手并不是一个好的选择。它虽然运行环境和代码量小巧,但在工程层面实际是个大项目,背后还有无数的单元测试。它做到了软件开发的一个非常难做到的事情:100% 测试覆盖。这意味着后续的任何开发迭代都能做到机器自动化测试,自动找出新加入代码产生的兼容性问题,不得不说这是程序员梦想中的代码迭代方式。SQLite 一开始就诞生在工业界巨头公司的夹缝里面,从最开始它就是一个对运行质量要求极高的工业软件。它看起来像一个精致的小软件,但绝不是一个小玩具,当年他合作的是摩托罗拉和诺基亚这种巨头放在今天也是 facebook,google 这种体量。从它打败诺基亚内部 9 个同类产品这段经历来看,这是很典型的一个头部软件赢家通吃的案例。它是纯粹的商业驱动项目,并不是开源社区的项目,开源只是它商业模式的一部分。

1.SQlite 通过文件来保存数据库,一个文件就是一个数据库。 2.数据库里又包含数个表格; 3.每个表格里面包含了多个记录; 4.每个记录由多个字段组成; 5.每个字段都有其对应的值; 6.每个值都可以指定类型,并且指定约束。

数据类型

许多SQL数据库引擎(除SQLite之外的各种SQL数据库引擎)使用静态、严格的数据类型。对于静态类型,一个值的数据类型由它的容器,即存储这个值的列来决定。

SQLite则使用更加通用的动态类型系统。在SQLite中,一个值的数据类型被关联到这个值本身,而不是它的容器。

SQLite的动态类型系统向后兼容一般静态类型系统的数据库引擎。在某种意义上,工作在静态类型数据库上的SQL声明也同样能工作在SQLite上。

但是SQLite动态类型还允许做一些在传统严格类型的数据库中不能做的事情。

\ No newline at end of file diff --git "a/advanced/1.1.3InnoDB\342\200\224\342\200\224\345\264\251\346\272\203\346\201\242\345\244\215/index.html" "b/advanced/1.1.3InnoDB\342\200\224\342\200\224\345\264\251\346\272\203\346\201\242\345\244\215/index.html" new file mode 100644 index 000000000..11343fd30 --- /dev/null +++ "b/advanced/1.1.3InnoDB\342\200\224\342\200\224\345\264\251\346\272\203\346\201\242\345\244\215/index.html" @@ -0,0 +1,97 @@ + 1.1.3InnoDB——崩溃恢复 - MySQL WIKI
跳转至

1.1.3InnoDB——崩溃恢复

数据更新流程

当我们查询数据的时候,会先去Buffer Pool中查询。如果Buffer Pool中不存在,存储引擎会先将数据从磁盘加载到Buffer Pool中,然后将数据返回给客户端;

同理,当我们更新某个数据的时候,如果这个数据不存在于Buffer Pool,同样会先数据加载进来,然后修改修改内存的数据。被修改过的数据会在之后统一刷入磁盘。

假设我们修改Buffer Pool中的数据成功,但是还没来得及将数据刷入磁盘MySQL就挂了怎么办?按照上图的逻辑,此时更新之后的数据只存在于Buffer Pool中,如果此时MySQL宕机了,这部分数据将会永久的丢失;

redo和undo

MySQL能够实现崩溃恢复的**事实**来看,MySQL必定实现了某些骚操作。没错,这就是接下来我们要介绍的另外的两个关键功能,Redo Log**和**Undo Log

这两种日志是属于InnoDB存储引擎的日志,和MySQL Server的Binlog不是一个维度的日志。

  1. Redo Log 记录了此次事务 「完成后」 的数据状态,记录的是更新之 「后」 的值
  2. Undo Log 记录了此次事务 「开始前」 的数据状态,记录的是更新之 「前」 的值

这两种日志有明显的区别。

更新数据还是会判断数据是否存在于Buffer Pool中,不存在则加载进来。上面我们提到了回滚的问题,在更新Buffer Pool中的数据之前,我们需要先将该数据事务开始之前的状态写入Undo Log中。假设更新到一半出错了,我们就可以通过Undo Log来回滚到事务开始前。

然后执行器会更新Buffer Pool中的数据,成功更新后会将数据最新状态写入Redo Log Buffer中。因为一个事务中可能涉及到多次读写操作,写入Buffer中分组写入,比起一条条的写入磁盘文件,效率会高很多。

undo

undo log 是mysql中比较重要的事务日志之一undo log是一种用于撤销回退的日志。

在一个事务没提交之前,MySQL会先记录更新前的数据到 undo log日志文件里面,当事务回滚时或者数据库崩溃时,可以利用 undo log来进行回退。

在MySQL中,undo log日志的作用主要有两个:

1、提供回滚操作【undo log实现事务的原子性】

在设计DB时,我们假设数据库可能在任何时刻,由于如硬件故障,软件Bug,运维操作等原因突然崩溃。

这个时候尚未完成提交的事务可能已经有部分数据写入了磁盘,如果不加处理,会违反数据库对Atomic的保证,也就是任何事务的修改要么全部提交,要么全部取消。

针对这个问题,直观的想法是等到事务真正提交时,才能允许这个事务的任何修改落盘,也就是No-Steal策略。显而易见,这种做法一方面造成很大的内存空间压力,另一方面提交时的大量随机IO会极大的影响性能。

因此,数据库实现中通常会在正常事务进行中,就不断的连续写入Undo Log,来记录本次修改之前的历史值。

当Crash真正发生时,可以在Recovery过程中通过回放Undo Log将未提交事务的修改抹掉。InnoDB采用的就是这种方式。

我们在进行数据更新操作的时候,不仅会记录redo log,还会记录undo log,如果因为某些原因导致事务回滚,那么这个时候MySQL就要执行回滚(rollback)操作,利用undo log将数据恢复到事务开始之前的状态。

例如如我们执行下面一条删除语句:

delete from user where id = 1;
+

那么此时undo log会记录一条对应的insert 语句【反向操作的语句】,以保证在事务回滚时,将数据还原回去。

如果这个修改出现异常,可以使用undo log日志来实现回滚操作,以保证事务的一致性。

2、MVCC,即多版本控制。在MySQL数据库InnoDB存储引擎中,用undo Log来实现多版本并发控制(MVCC)。当读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录以前的数据版本是怎样的,从而让用户能够读取到当前事务操作之前的数据【快照读】。

下面解释一下什么是快照读,与之对应的还有一个是---当前读。

快照读:

SQL读取的数据是快照版本【可见版本】,也就是历史版本,不用加锁,普通的SELECT就是快照读。

当前读:

SQL读取的数据是最新版本。通过锁机制来保证读取的数据无法通过其他事务进行修改UPDATE、DELETE、INSERT、SELECT … LOCK IN SHARE MODE、SELECT … FOR UPDATE都是当前读。

undo表空间和存储机制

所谓表空间其实是真实存在于磁盘上的数据文件。而这里的所说的 undolog表空间 其实就是磁盘上专门存放 undolog 的文件。

在MySQL5.5以及之前,InnoDB的undo log也是存放在 ibdata1 里面的。一旦出现大事务,这个大事务所使用的undolog占用的空间就会一直在ibdata1里面存在,即使这个事务已经关闭。

MySQL从8.0开始undo 表空间管理已经发生了改变,在5.7版本中一旦MySQL初始化以后,就不能再改变undo表空间了。

所以我们在5.7版本中都是在初始化的时候对undo表空间进行一些设置,类似这样:在my.cnf文件中加入innodb_undo_directory= /data/mysql/undologs 和 innodb_undo_tablespaces=5 这两个参数。

之所以这么改,是因为我们想把undo表空单独从系统表空间idbdata中分离出来,这样就可以消除因undo的问题造成对ibdata系统表空间的影响。

所以上面的参数配置在5.7版本中是我们对MySQL初始化做的一个常规的最佳实践设置,如果不设置,那么在5.7版本中,undo还是默认会放在ibdata中。

从MySQL8.0版本开始,MySQL默认对undo进行了分离操作,使用默认配置初始化时,就会在datadir目录下生成两个10MiB大小的undo表空间文件undo_001 和 undo002

默认至少初始化2个Undo表空间,最大支持127个Undo表空间,默认表空间名称为undo_001,undo_002

因为大事务可能会导致单个undo文件变的很大,创建额外的表空间文件可以避免这个文件,8.0.14 之后UNDO表空间支持**在线扩缩容**

CREATE UNDO TABLESPACE tablespace_name ADD DATAFILE 'file_name.ibu';
+-- 不支持指定相对路径,只支持绝对路径,且必须是innodb_directories参数定义可识别的路径或默认的数据目录下
+
    • 不支持指定相对路径,只支持绝对路径,且必须是innodb_directories参数定义可识别的路径或默认的数据目录下
  • 动态创建的undo表空间必须以.ibu结尾
# 8.0.23之前,undo初始化大小依赖于inoodb_page_size,对于默认的16KB的页,undo默认是10MiB
+
# 
+
+mysql> show VARIABLES like '%undo%';
++--------------------------+------------+
+| Variable_name            | Value      |
++--------------------------+------------+
+| innodb_max_undo_log_size | 1073741824 |
+| innodb_undo_directory    | ./         |
+| innodb_undo_log_encrypt  | OFF        |
+| innodb_undo_log_truncate | ON         |
+| innodb_undo_tablespaces  | 2          |
++--------------------------+------------+
+5 rows in set (0.01 sec)
+
+mysql>
+
+
+show variables like '%undo%';
++--------------------------+------------+
+| Variable_name            | Value      |
++--------------------------+------------+
+| innodb_max_undo_log_size | 8589934592 |   
+| innodb_undo_directory    | ./         |  
+| innodb_undo_log_encrypt  | OFF        |
+| innodb_undo_log_truncate | ON         |
+| innodb_undo_tablespaces  | 2          |
++--------------------------+------------+
+
+show variables like '%truncate%';
++--------------------------------------+-------+
+| Variable_name                        | Value |
++--------------------------------------+-------+
+| innodb_purge_rseg_truncate_frequency | 128   |
+| innodb_undo_log_truncate             | ON    |
++--------------------------------------+-------+
+
+show variables like '%segment%';
++-------------------------------+-----------+
+| Variable_name                 | Value     |
++-------------------------------+-----------+
+| innodb_rollback_segments      | 128       |
+| innodb_segment_reserve_factor | 12.500000 |
++-------------------------------+-----------+
+
+innodb_undo_log_truncate    --控制是否自动做UNDO的truncate收缩操作,默认为ON,只有为ON时,下面2个参数才生效
+innodb_max_undo_log_size    --控制UNDO做truncate收缩操作的阈值,当UNDO达到该值时才出发收缩操作
+innodb_purge_rseg_truncate_frequency 
+        -- Batch UNDO清理的次数,默认最大值128,也就是128次后才会触发一次UNDO的truncate,而每次清理的undo page由innodb_purge_batch_size参数决定,innodb_purge_batch_size默认为300,也就是300*128个UNDO小批次清理后才会触发UNDO表空间的truncate(也就是UNDO表空间的收缩)操作
+
+innodb_undo_tablespaces
+-- 控制生成的UNDO表空间的数量,默认2个,在8.0对该参数做了废弃,但并未提供其他参数控制UNDO数量,当前依旧可以使用该参数做UNDO表空间数量配置,通常建议配置为3(手工收缩UNDO时需要至少3个UNDO表空间)
+
+innodb_rollback_segments            -- UNDO表空间回滚段的数量,默认为最大值128
+

UNDO 表空间运维

-- 可以查看到undo的表空间名称/文件路径/初始大小/扩展大小/磁盘文件大小/可用空间及是否启用的状态等
+SELECT T1.SPACE AS SPACE_ID,
+       T1.NAME AS TABLESPACE_NAME,
+       T2.FILE_NAME,
+       ROUND(T2.INITIAL_SIZE / 1024 / 1024, 2) AS "INITIAL_SIZE(M)",
+       ROUND(T2.AUTOEXTEND_SIZE / 1024 / 1024, 2) AS "AUTOEXTEND_SIZE(M)",
+       ROUND(T1.FILE_SIZE / 1024 / 1024, 2) AS "FILE_SIZE_DISK(M)",
+       ROUND(T2.DATA_FREE / 1024 / 1024, 2) AS "DATA_FREE(M)",
+       T2.STATUS,
+       T1.STATE
+  FROM INFORMATION_SCHEMA.INNODB_TABLESPACES T1,
+       INFORMATION_SCHEMA.FILES              T2
+ WHERE T1.SPACE = T2.FILE_ID
+   AND T1.ROW_FORMAT = 'Undo';
+
+
+
+
+
+-- 创建一个新的UNDO表空间
+CREATE UNDO TABLESPACE undo_004 ADD DATAFILE 'undo_004.ibu';
+
+-- 可以用前面的命令查看创建后的状态
+
+-- 可以将已有的UNDO表示为inactive(也可理解为UNDO表空间收缩)
+-- PS:设置为INACTIVE的表空间的STATE为empty,表示这个表空间不包含任何事务回滚数据,且表空间也收缩为默认大小
+ALTER UNDO TABLESPACE undo_003 SET INACTIVE;
+
+-- 可以将inactive的UNDO转为active
+ALTER UNDO TABLESPACE innodb_undo_001 SET ACTIVE;
+
+-- 可以将inactive的UNDO表空间进行删除
+-- PS:默认以innodb_开头初始化的undo表空间不可被删除
+DROP UNDO TABLESPACE innodb_undo_001;
+ERROR: 3119 (42000): InnoDB: Tablespace names starting with `innodb_` are reserved.
+
+-- 非系统默认的UNDO在inactive后可被删除
+ALTER UNDO TABLESPACE undo_003 SET ACTIVE;
+Query OK, 0 rows affected (0.0030 sec)
+

影响UNDO inactive(truncate)性能的因素

  • UNDO 表空间的大小
  • UNDO 表空间的数量
  • UNDO LOGS的数量(实际INSERT/UPDATE/DELETE这类事务回滚段的数据量)
  • 磁盘IO的能力/当前系统的负载
  • 是否存在长事务在使用该UNDO表空间

PS:通常对表空间做收缩前最简单避免性能的方式是提前创建一个UNDO表空间,收缩完后再删除或一直保留均可

\ No newline at end of file diff --git "a/advanced/1.2.1.Innodb\342\200\224\342\200\224\345\217\214\345\206\231\347\274\223\345\206\262\345\214\272DoublewriteBuffer/index.html" "b/advanced/1.2.1.Innodb\342\200\224\342\200\224\345\217\214\345\206\231\347\274\223\345\206\262\345\214\272DoublewriteBuffer/index.html" new file mode 100644 index 000000000..fcb3d6a45 --- /dev/null +++ "b/advanced/1.2.1.Innodb\342\200\224\342\200\224\345\217\214\345\206\231\347\274\223\345\206\262\345\214\272DoublewriteBuffer/index.html" @@ -0,0 +1,11 @@ + Doublewrite架构 - MySQL WIKI
跳转至

Doublewrite架构

Doublewrite Buffer被MySQL放在官方文档目录的 "InnoDB On-Disk Structures" 中了,但真实情况是Doublewrite Buffer是内存+磁盘的结构。

背景

操作系统可以看成是一个程序,作为程序而言,都有最小处理单位的说法,我们常见的服务器一般都是Linux操作系统,对应文件系统的页(OS Page)就可以看成是Linux操作系统与文件系统交互的最小单位。

一般情况下,除了操作系统的页(OS Page)为4KB之外,其余程序的页(Page)都会大于等于操作系统的页大小,比如,Oracle的Page大小为8KB。

MySQL的Page大小也可以通过上面innodb_page_size参数指定,具体情况如下:

  • <5.6版本时,不可调整;
  • 5.6版本时,可自定义为8KB、4KB,但不能调大【调小是为了调高数据对齐概率】;
  • 5.7以上,才可以改成32KB、64KB【且最好设置成默认值16KB的整数倍,这是全局选项,无法在MySQL运行过程中动态修改】。

说了这么多,其实大多数情况一般都不用修改,使用默认值即可。

有点扯远了。我们知道操作系统的页大小和MySQL的页大小了,而且MySQL程序是跑在Linux操作系统上的,所以可以得出如下结论:

MySQL将Buffer Pool中一页数据刷入磁盘,要写4个文件系统里的页(也可以说成一个MySQL数据页映射4个系统页)

Doublewrite解决了什么问题?

为了解决**部分页面写入**问题(Partial Page Write)。

MySQL写入修改时刷新整个页面(默认16KB),而不仅仅是刷新页面中已更改的记录。而系统的单次io,一般是512byte为单位的,在断电,OS crash(操作系统崩溃)情况下可能会丢失数据。

Doublewrite是指哪两次写入?

  1. 写Doublewrite buffer,注意: Doublewrite buffer**是磁盘不是内存**。
  2. 写入数据文件。

写入顺序:先写doublewrite buffer,写**成功后**再写到数据文件。

Doublewrite buffer存储区位于什么地方?

# 指定df文件的存储路径,默认是跟innodb_data_home_dir,一般就是数据目录datadir
+innodb_doublewrite_dir=innodb_data_home_dir
+
+# 指定db文件的数量,默认为每个buffer_pool_instance创建2个,最小是2,最大是256
+innodb_doublewrite_files=
+

innodb 什么时候将脏页写入Doublewrite buffer中?

由以下几个参数决定:

  • innodb_max_dirty_pages_pct_lwm: 低位水平标记,达到该值将启动缓冲刷新,默认为10(百分比,脏页/缓冲池)
  • innodb_max_dirty_pages_pct: 脏页数量与缓冲池比例阈值,默认为90
  • 如果开启自适应刷新(Adaptive Flushing)InnoDB根据重做日志生成的速度和当前的刷新率,使用自适应刷新算法来动态调整刷新率。

如何查看双写缓冲区的大小?

8.0.20之前,Doublewrite buffer是系统表空间中连续的128个页(每个页16k),总共2M

8.0.20开始的doublewrite buffer由单独文件保存:

# Doubliewrite文件命名
+#  #_页大小_文件编号.dblwr
+# 页大小一般默认就是16KB,由innodb_page_size控制
+-rwxrwxrwx 1 root root 192K  7月  6 23:15 '#ib_16384_0.dblwr'
+-rwxrwxrwx 1 root root 8.2M  7月  6 23:16 '#ib_16384_1.dblwr'
+

Doublewrite buffer从脏页刷新速度由哪些参数控制?

清洗线程数由innodb_page_cleaners配置(8.0.20版本默认为4),单个线程最多每次写入doublewrite buffer 的页面数由innodb_doublewrite_pages变量控制, 在默认情况下为innodb_write_io_threads的值(默认4), 每秒写入次数还受到innodb_io_capacity (定义了每秒I / O操作数,默认值200)和innodb_io_capacity_max(InnoDB在由后台任务每秒执行的最大IOPS数 )控制。

为什么有了redo log,还需要doublewrite buffer?

一般都认为,redo提供了崩溃恢复功能,

\ No newline at end of file diff --git "a/advanced/1.MySQL\346\236\266\346\236\204\345\210\260InnoDB\346\236\266\346\236\204/index.html" "b/advanced/1.MySQL\346\236\266\346\236\204\345\210\260InnoDB\346\236\266\346\236\204/index.html" new file mode 100644 index 000000000..42297f639 --- /dev/null +++ "b/advanced/1.MySQL\346\236\266\346\236\204\345\210\260InnoDB\346\236\266\346\236\204/index.html" @@ -0,0 +1,196 @@ + 1.MySQL架构到innoDB架构 - MySQL WIKI
跳转至

MySQL体系架构

数据库是数据的集合,数据库管理系统(DBMS)是操作和管理数据库的应用程序。数据库应用主要有两类:OLAP(联机分析处理)和OLTP(联机事务处理)。

OLAP的主要特点是:

  • 实时性要求不高
  • 数据量大
  • 并发量小

OLTP的主要特点是:

  • 实时性要求高
  • 数据量小
  • 高并发
  • 要求满足ACID

mysql是一种DBMS,其体系架构如下图所示:

1.0.1.MySQL架构图

mysql中集成的是插件式的存储引擎,InnoDB引擎是其中之一。存储引擎基于表而不是数据库:同一个数据库中根据不同表的访问操作需求可以选择不同的存储引擎。

InnoDB

InnoDB引擎主要面对OLTP类应用

InnoDB引擎在mysql中处于文件和文件系统的上层,管理着对InnoDB引擎表的访问和更新。

InnoDB架构图

MySQL5.7版本中的架构图:

1.0.2.InnoDB详细架构图

MySQL8.0 版本中的架构图:

innodb-architecture8.0.png

图片来源与官网:

https://dev.mysql.com/doc/refman/5.7/en/innodb-architecture.html

InnoDB的一个多线程模型

1.0.3.InnoDB多线程模型

Master Thread - 核心线程

以下内容全部引自: InnoDB存储引擎——Master Thread工作方式

(当做备份)

Master Thread是InnoDB存储引擎非常核心的一个后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲、UNDO页的回收等。

InnoDB 1.0.x版本之前的Master Thread

Master Thread具有 最高的线程优先级别

其内部由多个循环组成:主循环(loop)后台循环(backgroup loop)、刷新循环(flush loop)、暂停循环(suspend loop)。

Master Thread会 根据数据库运行的状态 在loop、backgroup loop、flush loop和suspend loop中 进行切换

loop是主循环,大多数的操作都在这个循环中,主要有两大部分的操作——每秒钟的操作和每10秒钟的操作。伪代码如下:

void master_thread()
+{
+    loop:
+    for(int i = 0; i < 10; ++i){
+        do thing once per second;
+        sleep 1 second if necessary;
+    }
+    do things once per ten seconds;
+    goto loop;
+}
+

每秒一次的操作包括:

  1. 日志缓冲刷新到磁盘,即使这个事务还没有提交(总是)

即使某个事务还没有提交,InnoDB存储引擎仍然每秒会将重做日志缓冲中的内容刷新到重做日志文件。这也解释了为什么再大的事务提交的时间也是很短的。

  1. 合并插入缓冲(可能)

合并插入缓冲并不是每秒都会发生的。InnoDB存储引擎会判断当前一秒内发生的IO次数是否小于5次,如果小于5次,InnoDB存储引擎认为当前的IO压力很小,可以执行合并插入缓冲的操作;

  1. 至多刷新100个InnoDB的缓冲池中的脏页到磁盘(可能); 刷新100个脏页也不是每秒都会发生的,InnoDB存储引擎通过判断当前缓冲池中脏页的比例(buf_get_modified_ratio_pct)是否超过了配置文件中 innodb_max_dirty_pages_pct这个参数(默认是75,代表75%),如果超过了这个值,InnoDB存储引擎则认为需要做磁盘同步的操作,将100个脏页写入磁盘中。

  2. 如果当前没有用户活动,则切换到background loop(可能);

综上所述,伪代码可以进一步具体化。

void master_thread()
+{
+    loop:
+    for(int i = 0; i < 10; ++i){
+        thread_sleep(1);
+        do log buffer flush to disk;
+        if(last_one_second_ios < 5)
+            do merge at most 5 insert buffer;
+        if(buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct)
+            do buffer pool flush 100 dirty page;
+        if(no user activity)
+            goto backgroud loop;
+    }
+    do things once per ten seconds;
+    backgroud loop;
+    do something;
+    goto loop;
+}
+

每10秒的操作主要是下面几个方面:

  1. 刷新100个脏页到磁盘(可能) InnoDB存储引擎会先判断过去10秒之内磁盘的IO操作是否小于200次,如果是,InnoDB存储引擎认为当前有足够的磁盘IO能力,因此将100个脏页刷新到磁盘。

  2. 合并至多5个插入缓冲(总是)

  3. 将日志缓冲刷新到磁盘(总是)

  4. 删除无用的Undo页(总是)

  5. 刷新100个或者10个脏页到磁盘(总是) InnoDB存储引擎会执行full purge操作,即删除无用的Undo页。对表进行update,delete这类的操作时,原先的行被标记为删除,但是因为一致性读的关系,需要保留这些行版本的信息。但是在full purge过程中,InnoDB存储引擎会判断当前事务系统中已被删除的行是否可以删除,比如有时候可能还有查询操作需要读取之前版本的undo信息,如果可以删除,InnoDB存储引擎会立即将其删除。从源代码中可以看出,InnoDB存储引擎在执行full purge 操作时,每次最多尝试回收20个undo页。

然后,InnoDB存储引擎会判断缓冲池中脏页的比例(buf_get_modified_ratio_pct),如果有超过70%的脏页,则刷新100个脏页到磁盘,如果脏页的比例小于70%,则只需刷新10%的脏页到磁盘。

伪代码进一步细化:

void master_thread()
+{
+    loop:
+    for(int i = 0; i < 10; ++i){
+        thread_sleep(1);
+        do log buffer flush to disk;
+        if(last_one_second_ios < 5)
+            do merge at most 5 insert buffer;
+        if(buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct)
+            do buffer pool flush 100 dirty page;
+        if(no user activity)
+            goto backgroud loop;
+    }
+    if(last_ten_second_ios < 200)
+        do buffer pool flush 100 dirty page;
+
+    do merge at most 5 insert buffer;
+    do log buffer flush to disk;
+    do full purge;
+    if(buf_get_modified_ratio_pct > 70%)
+        do buffer pool flush 100 dirty page;
+    else
+        buffer pool flush 10 dirty page;
+    goto loop;
+    backgroud loop;
+    do something;
+    goto loop;
+}
+

如果当前没有用户活动(数据库空闲)或者数据库关系,就会切换到backgroud loop这个循环。 backgroud loop会执行以下操作:

  1. 删除无用的Undo页(总是)
  2. 合并20个插入缓冲(总是)
  3. 跳回到主循环(总是)
  4. 不断刷新100个页直到符合条件(可能,需要跳转到flush loop中完成)

如果flush loop中也没有什么事情可以做了,InnoDB存储引擎会切换到suspend_loop,将Master Thread挂起,等待事件的发生。若用户启用了InnoDB存储引擎,却没有使用任何InnoDB存储引擎的表,那么Master Thread总是处于挂起的状态。

最后,Master Thread完整的伪代码如下:

void master_thread()
+{
+    loop:
+    for(int i = 0; i < 10; ++i){
+    thread_sleep(1);                            // sleep 1秒
+        do log buffer flush to disk;
+        if(last_one_second_ios < 5)
+            do merge at most 5 insert buffer;
+        if(buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct) 
+                                              // 如果缓冲池中的脏页比例大于innodb_max_dirty_pages_pct(默认是75时)
+            do buffer pool flush 100 dirty page;    // 刷新100脏页到磁盘
+        if(no user activity)
+            goto backgroud loop;
+    }
+    if(last_ten_second_ios < 200)   // 如果过去10内磁盘IO次数小于设置的innodb_io_capacity的值(默认是200)
+        do buffer pool flush 100 dirty page;
+
+    do merge at most 5 insert buffer;   // 合并插入缓冲是innodb_io_capacity的5%(10)(总是)
+    do log buffer flush to disk;
+    do full purge;
+    if(buf_get_modified_ratio_pct > 70%)
+        do buffer pool flush 100 dirty page;
+    else
+        buffer pool flush 10 dirty page;
+
+    backgroud loop: // 后台循环
+    do full purge   // 删除无用的undo页 (总是)
+    do merge 20 insert buffer;  // 合并插入缓冲是innodb_io_capacity的5%(10)(总是)
+    if not idle                 // 如果不空闲,就跳回主循环,如果空闲就跳入flush loop
+        goto loop:              // 跳到主循环
+    else
+        goto flush loop
+
+    flush loop:                 // 刷新循环
+    do buffer pool flush 100 dirty page;
+    if(buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct) 
+                                // 如果缓冲池中的脏页比例大于innodb_max_dirty_pages_pct的值(默认75%)
+        goto flush loop;        // 跳到刷新循环,不断刷新脏页,直到符合条件
+
+        goto suspend loop;      // 完成刷新脏页的任务后,跳入suspend loop
+
+    suspend loop:
+    suspend_thread();           //master线程挂起,等待事件发生
+    waiting event;
+    goto loop;
+}
+

InnoDB 1.2.x版本之前的Master Thread

1.0.x版本中,InnoDB存储引擎最多只会刷新100个脏页到磁盘,合并20个插入缓冲。如果是在写入密集的应用程序中,每秒可能会产生大于100个的脏页,如果是产生大于20个插入缓冲的情况,那么可能会来不及刷新所有的脏页以及合并插入缓冲。

后来,InnoDB存储引擎提供了参数innodb_io_capacity,用来表示磁盘IO的吞吐量,默认值为200。

mysql> show variables like 'innodb_io_capacity';
++--------------------+-------+
+| Variable_name      | Value |
++--------------------+-------+
+| innodb_io_capacity | 200   |
++--------------------+-------+
+1 row in set (0.00 sec)
+

对于刷新到磁盘的页的数量,会按照innodb_io_capacity的百分比来进行控制。规则如下:

  1. 在合并插入缓冲时,合并插入缓冲的数量为innodb_io_capacity值的5%;
  2. 在从缓冲区刷新脏页时,刷新脏页的数量为innodb_io_capacity;

如果用户使用的是SSD类的磁盘,可以将innodb_io_capacity的值调高,直到符合磁盘IO的吞吐量为止;

另一个问题是参数innodb_max_dirty_pages_pct的默认值,在1.0.x版本之前,该值的默认值是90,意味着脏页占缓冲池的90%。InnoDB存储引擎在每秒刷新缓冲池和flush loop时会判断这个值,如果该值大于innodb_max_dirty_pages_pct,才会刷新100个脏页,如果有很大的内存,或者数据库服务器的压力很大,这时刷新脏页的速度反而会降低。 后来将innodb_max_dirty_pages_pct的默认值改为了75。这样既可以加快刷新脏页的频率,又能够保证磁盘IO的负载。

mysql> show variables like 'innodb_max_dirty_pages_pct';
++----------------------------+-------+
+| Variable_name              | Value |
++----------------------------+-------+
+| innodb_max_dirty_pages_pct | 75    |
++----------------------------+-------+
+1 row in set (0.00 sec)
+

还有一个新的参数是innodb_adaptive_flushing(自适应地刷新),该值影响每秒刷新脏页的数量。原来的刷新规则是:脏页在缓冲池所占的比例小于innodb_max_dirty_pages_pct时,不刷新脏页;大于innodb_max_dirty_pages_pct时,刷新100个脏页。随着innodb_adaptive_flushing参数的引入,InnoDB通过一个名为buf_flush_get_desired_flush_rate的函数来判断需要刷新脏页最合适的数量。buf_flush_get_desired_flush_rate函数通过判断产生重做日志的速率来决定最合适的刷新脏页数量。

之前每次进行full purge 操作时,最多回收20个Undo页,从InnoDB 1.0.x版本开始引入了参数innodb_purge_batch_size,该参数可以控制每次full purge回收的Undo页的数量。该参数的默认值为20,并可以动态地对其进行修改。

mysql> show variables like 'innodb_purge_batch_size';
++-------------------------+-------+
+| Variable_name           | Value |
++-------------------------+-------+
+| innodb_purge_batch_size | 20    |
++-------------------------+-------+
+1 row in set (0.00 sec)
+

Master Thread的伪代码变为了下面的形式:

void master_thread()
+{
+    loop:
+    for(int i = 0; i < 10; ++i){
+        thread_sleep(1);
+        do log buffer flush to disk;
+        if(last_one_second_ios < 5%innodb_io_capacity)
+            do merge 5%innodb_io_capacity insert buffer;
+        if(buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct)
+            do buffer pool flush 100%innodb_io_capacity dirty page;
+        else if enable adaptive flush
+            do buffer pool flush desired amount dirty page;
+        if(no user activity)
+            goto backgroud loop;
+    }
+    if(last_ten_second_ios < innodb_io_capacity)
+        do buffer pool flush 100%innodb_io_capacity dirty page;
+
+    do merge 5%innodb_io_capacity insert buffer;
+    do log buffer flush to disk;
+    do full purge;
+    if(buf_get_modified_ratio_pct > 70%)
+        do buffer pool flush 100%innodb_io_capacity dirty page;
+    else
+        do buffer pool flush 10%innodb_io_capacity dirty page;
+
+    goto loop;
+    backgroud loop:
+    do full purge
+    do merge 100%innodb_io_capacity insert buffer;
+    if not idle
+        goto loop:
+    else
+        goto flush loop
+
+    flush loop:
+    do buffer pool flush 100%innodb_io_capacity dirty page;
+    if(buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct)
+        goto flush loop;
+
+    goto suspend loop;
+
+    suspend loop:
+    suspend_thread();
+    waiting event;
+    goto loop;
+}
+

这个版本的性能得到了提高。

mysql> show engine innodb status\G
+*************************** 1. row ***************************
+  Type: InnoDB
+  Name: 
+Status: 
+=====================================
+170312 20:14:04 INNODB MONITOR OUTPUT
+=====================================
+Per second averages calculated from the last 38 seconds
+-----------------
+BACKGROUND THREAD
+-----------------
+srv_master_thread loops: 1 1_second, 1 sleeps, 0 10_second, 1 background, 1 flush
+srv_master_thread log flush and writes: 1
+

可以看到主循环进行了1次,每秒的操作进行了1次,10秒一次的操作进行了0次,backgound loop进行了1次,flush loop进行了1次。

InnoDB 1.2.x版本的Master Thread

1.2.x版本中再次对Master Thread进行了优化。 Master Thread的伪代码如下:

if InnoDB is idle
+    srv_master_do_idle_tasks();
+else
+    srv_master_do_active_tasks();
+

其中srv_master_do_idle_tasks()就是之前版本中每10秒的操作,srv_master_do_active_tasks()处理的是之前每秒中的操作。同时,对于刷新脏页的操作,从Master Thread线程分离到一个单独的Page Cleaner Thread,从而减轻了Master Thread的工作,同时进一步提高了系统的并发性。

IO Thread

InnoDB中大量使用**AIO (Async IO)** 来处理IO请求。

IO Thread的作用,是负责这些 IO 请求的回调(call back)

可使用 show engine innodb status看到以下类型

  • insert buffer thread
  • log thread
  • read thread(4个)
  • write thread(4个)

Purge Thread

作用

事务被提交后,其所使用的undo log可能不在需要。因此,需要purge thread来回收已经使用并分配的undo页。

来历

以前Master Thread来完成释放undo log,InnoDB1.1独立出来,分担主线程压力

查看purge thread的数量

mysql> show variables like 'innodb_purge_threads';
++----------------------+-------+
+| Variable_name        | Value |
++----------------------+-------+
+| innodb_purge_threads | 4     |
++----------------------+-------+
+1 row in set (0.02 sec)
+

Page Cleaner Thread

作用

**脏页**刷新到磁盘

来历

以前Master Thread来刷新脏页,InnoDB1.2独立出来,分担主线程压力

\ No newline at end of file diff --git "a/advanced/2.InnoDB\342\200\224\342\200\224\347\256\200\344\273\213/index.html" "b/advanced/2.InnoDB\342\200\224\342\200\224\347\256\200\344\273\213/index.html" new file mode 100644 index 000000000..b449cead4 --- /dev/null +++ "b/advanced/2.InnoDB\342\200\224\342\200\224\347\256\200\344\273\213/index.html" @@ -0,0 +1,4 @@ + 2.InnoDB——简介 - MySQL WIKI
跳转至

InnoDB简介

使用InnoDB的好处

  1. 如果您的服务器由于硬件或软件问题而意外退出,无论当时数据库中发生了什么,重新启动数据库后都不需要执行任何特殊操作。InnoDB崩溃恢复会自动提交 崩溃之前已提交 的所有事务并落盘持久化,并撤消所有 正在处理但尚未提交 的事务。只需重新启动并从上次中断的地方继续即可。

  2. InnoDB存储引擎有自己的缓冲池,在访问数据时可以在主存中缓存 索引数据 。经常使用的数据直接从 内存 中处理。此缓存适用于许多类型的信息,并加快处理速度。在专用数据库服务器上,通常会将高达 80% 的物理内存分配给缓冲池。

  3. 如果将相关数据分割到不同的表中,则可以设置执行参照完整性的外键。更新或删除数据,其他表中的相关数据将 自动 更新或删除。如果尝试将数据插入到一个辅助表中,而主表中没有相应的数据,那么 错误的数据将自动被踢出

  4. 如果数据在磁盘或内存中损坏,则校验机制会在使用前提醒您注意虚假数据。

  5. 当您为每个表使用适当的主键列设计数据库时,涉及这些列的操作将 自动优化 。在 WHERE 子句、 ORDER BY 子句、 GROUP BY 子句和 join操作 中引用主键列非常快。

  6. 插入、更新和删除由一种称为change buffering的自动机制进行优化。InnoDB不仅允许对同一个表并发读和写访问,它还 缓存修改后的数据 以简化磁盘I/O。

  7. 自适应哈希索引。当从一个表中一遍又一遍访问相同的数据时,InnoDB会自动对这些数据建立自适应哈希索引,就像从哈希表中查出来一样。

  8. 您可以压缩表和关联的索引。

  9. 您可以创建和删除索引,而对性能和可用性的影响要小得多。

  10. 截断表空间文件非常快,可以释放磁盘空间供操作系统重用,而不是释放只有InnoDB才能重用的系统表空间内的空间。

  11. 对于BLOB和长TEXT字段,使用动态行格式的表数据的存储布局更有效。

  12. 您可以通过查询INFORMATION_SCHEMA表来监视存储引擎的内部工作。

  13. 您可以通过查询性能模式表来监视存储引擎的性能细节。

  14. 您可以自由地将InnoDB表与其他MySQL存储引擎的表混合使用,即使在同一条语句中也是如此。例如,您可以使用JOIN操作在单个查询中合并来自InnoDBMEMORY表的数据。

  15. InnoDB是为处理大数据量时的CPU效率和最高性能而设计的。

  16. InnoDB表可以处理大量数据,即使是在文件大小限制在2GB的操作系统上。

如何使InnoDB性能更高

  1. 为每个表中查询最频繁的一列或几列指定一个主键,如果没有明显的主键,则指定一个 自动递增的值

  2. 使用join连接从多个表中根据相同的ID值提取数据时,为了提高连接性能,可以在连接列上定义外键,并在每个表中使用 相同的数据类型 声明这些列。 添加外键可以确保引用的列被索引,这可以提高性能。外键还将 删除更新 传播到所有受影响的表,并防止在父表中没有对应id的情况下在子表中插入数据。

  3. 关闭自动提交。每秒提交数百次会限制性能(受存储设备的写入速度限制)。

  4. 通过使用START TRANSACTIONCOMMIT语句将相关的DML操作sql分组到事务中。不建议过于频繁地提交,也不建议发出一个批次中含有大量的INSERT、UPDATE或DELETE语句,这些语句可能运行几个小时。

  5. 不建议使用LOCK TABLES 语句。InnoDB可以同时处理对同一个表进行读写的多个会话,而无需牺牲可靠性或高性能。要获得对一组行的排他性写访问权限,请使用 SELECT ... FOR UPDATE语法仅锁定要更新的行。

  6. 启用innodb_file_per_table选项,或者使用常规表空间将表的数据索引放在 单独的文件 中,而不是系统表空间中。 默认情况下innodb_file_per_table选项是启用的。

  7. 评估您的数据和访问模式是否可以从InnoDB表或页面压缩功能中受益。您可以在InnoDB不牺牲读/写功能的情况下压缩表。

  8. 使用选项sql_mode=NO_ENGINE_SUBSTITUTION运行服务器,以防止在CREATE TABLE的engine =子句中指定的引擎出现问题时使用不同的存储引擎创建表。

MySQL所有存储引擎

可以通过如下SQL语句来查询MySQL支持的各种存储引擎

show engines;
+
+SELECT * FROM INFORMATION_SCHEMA.ENGINES;
+
Engine Support Comment Transactions XA Savepoints
InnoDB DEFAULT Supports transactions, row-level locking, and foreign keys YES YES YES
MRG_MYISAM YES Collection of identical MyISAM tables NO NO NO
MEMORY YES Hash based, stored in memory, useful for temporary tables NO NO NO
BLACKHOLE YES /dev/null storage engine (anything you write to it disappears) NO NO NO
MyISAM YES MyISAM storage engine NO NO NO
CSV YES CSV storage engine NO NO NO
ARCHIVE YES Archive storage engine NO NO NO
PERFORMANCE_SCHEMA YES Performance Schema NO NO NO
FEDERATED NO Federated MySQL storage engine NULL NULL NULL
\ No newline at end of file diff --git "a/advanced/3.InnoDB\342\200\224\342\200\224\345\205\263\351\224\256\347\211\271\346\200\247/index.html" "b/advanced/3.InnoDB\342\200\224\342\200\224\345\205\263\351\224\256\347\211\271\346\200\247/index.html" new file mode 100644 index 000000000..3840fb38c --- /dev/null +++ "b/advanced/3.InnoDB\342\200\224\342\200\224\345\205\263\351\224\256\347\211\271\346\200\247/index.html" @@ -0,0 +1,65 @@ + 3.InnoDB关键特性 - MySQL WIKI
跳转至

InnoDB的关键特性

Insert Buffer - 插入缓冲

以下内容引自 InnoDB的关键特性

回顾一下,InnoDB是索引组织表,主键是唯一标识。

在数据量较小的系统中经常使用的自增ID做为主键,利用数据库的自增ID,从1开始,基本可以做到连续递增。

但是对于有多个二级索引的表,二级索引的叶子节点的数据插入,就不是顺序的了。

Insert Buffer的作用

插入缓冲是为了 提高插入数据效率 的,在往非聚簇索引中插入数据时,首先会看缓冲池中,是否有要插入的非聚簇索引页。如果有则直接插入,那个页变成脏页。如果没有,就把**多次插入的数据先缓冲到插入缓冲中**,然后**合并多次操作**,即把非聚集索引在一起的数据合并为一次IO(减少IO),再以一定的频率刷新到磁盘(将Insert Buffer和辅助索引页字节点进行merge操作),但是插入缓冲只是针对**非聚集索引没有非唯一约束的索引**的插入有效。

(类似于MRR,随机IO变顺序IO)

什么是聚集索引/非聚集索引

InnoDB的数据是按主键的索引的顺序来存放数据的,这种索引被称为聚集索引,一个表有且只有一个聚集索引。但我们可以为一个表创建多个索引,这些索引被称为非聚集索引,或者辅助索引。这些索引是存放在另外的索引页的,跟实际数据的存放是没有关系的。非聚集索引记录的是索引列的数据,不是全部的列。

如果不了解聚集索引,有可能会被绕晕,但是,至少可以得出一个结论,就是聚集索引是按顺序存放的,而非聚集索引是没有必然的顺序的。这就导致一个问题,当插入一条数据时,更新非聚集索引时,必须到处去找非聚集索引所在的页(也称为离散写),这样的插入效率就比较低了。

为什么必须是没有唯一约束的

如果该非聚集索引有唯一约束,那么为了保证唯一性,必须每次插入前都去查询是否存在相同的数据,这时就必须到磁盘到处找是否存在该值(也称为离散读),这就导致插入缓冲失效了。

插入缓冲是为了解决非聚集索引随机写导致的效率低的问题,但是对于有唯一约束的非聚集索引也无能为力。

离散读/写效率真的很慢吗

在机械硬盘里,离散读必须要移动磁头,所以速度真的很慢,但是随着固态硬盘的发展,也许这个就不是问题了。固态硬盘的随机读写能力很强,而且现在固态硬盘的价格也低了不少。不过要让mysql支持固态硬盘,可能还有配置不少参数,直接上固态硬盘似乎效果不明显呢。

1.1.0.磁盘结构

带来的一个问题

在写密集的情况下,插入缓冲会占用过多的缓冲池内存,默认最大可以占用½的缓冲池内存,修正这个问题可以修改默认值对插入缓冲的大小进行控制。

  • innodb1.0.x版本:对insert buffer升级为change buffer,其使用的对象依然是非唯一的辅助索引,从这个版本开始,innodb存储引擎可以对**DML**(数据操作语言)操作---insert delete update**进行缓冲,分别是**insert bufferdelete bufferpurge buffer,并提供参数**innodb_change_buffering**用来开启各种buffer的选项,可选值为inserts、deletes、purges、changes(表示启用inserts和deletes)、all(表示启用所有)、none(表示都不启用),默认为all
mysql> show variables like 'innodb_change_buffering';
++-------------------------+-------+
+| Variable_name           | Value |
++-------------------------+-------+
+| innodb_change_buffering | all   |
++-------------------------+-------+
+1 row in set, 1 warning (0.00 sec)
+
  • innodb 1.2.x版本:通过参数innodb_change_buffer_max_size来控制change buffer最大使用内存的数量,默认为25,表示最多使用¼的缓冲池内存空间,该参数最大有效值为50.
mysql> show variables like 'innodb_change_buffer_max_size';
++-------------------------------+-------+
+| Variable_name                 | Value |
++-------------------------------+-------+
+| innodb_change_buffer_max_size | 25    |
++-------------------------------+-------+
+1 row in set, 1 warning (0.00 sec)
+

Insert Buffer内部实现

insert buffer内部是一颗B+树,mysql4.1之前是每张表有一颗insert buffer B+树,而现在的版本是全局只有一颗B+树,负责对所有的表的辅助索引进行insert buffer, 这颗B+树存放在共享表空间中,即ibdata1中。

Insert Buffer中B+树的非叶子节点中存放的是查询的serarch key(键值),结构如下图所示

+-------+--------+--------+
+| space | marker | offset |
++-------+--------+--------+
+

search key一共占有9个字节:

  • **space**表示待插入记录所在表的表空间id,占用4个字节,每个表有唯一的space id,可通过space id查询是哪张表,
  • **marker**占用1字节,用来兼容老版本的insert buffer,
  • **offset**表示页所在的偏移量,占用4字节。

当一个辅助索引要插入到页(space,offset)时,如果这个页不在缓冲池中,则按照上述结构构造一个search key,然后查询Insert Buffer的B+树,将这条记录插入到Insert BufferB+树中。

对于Insert Buffer的B+树的叶子节点,并非只有记录,而是如下所示:

+-------+--------+--------+----------+-----------+-----------+
+| space | marker | offset | metadata | 辅助索引值 |    ...    |
++-------+--------+--------+----------+-----------+-----------+
+

前面和非叶子结点的含义相同,一共占用9个字节,metadata占用4个字节,从第5列开始,就是实际插入记录的各个字段了,所以与原插入记录相比较,B+树的叶子节点记录需要额外记录13字节的开销

metadata中存储的内容

名称 字节 含义
IBUF_REC_OFFSET_COUONT 2 对每个进入Insert Buffer的记录进行排序
IBUF_REC_OFFSET_TYPE 1 每个进入Insert Buffer的记录类型
IBUF_REC_OFFSET_FLAGS 1 每个进入Insert Buffer的记录标记

Insert Buffer BitMap

因为启用Insert Buffer之后,辅助索引页(space,offset)可能被记录到Insert Buffer的B+树中,所以为了保证每次Merge Insert Buffer必须成功,需要一个特殊的页来标记每个辅助索引页(space,offset)的可用空间。

每个Insert Buffer BitMap页用来追踪16384个辅助索引,也就是256个区(Extent)。每个Insert Buffer BitMap在16384个页的第二页中。

相关知识点:

  • 每个区64页,一页16KB,一个区所占大小为: 16 * 64 = 1024KB = 1M
  • 256个区,一个区64页,则一个BiMap可以追踪的页为:256 * 64 = 16384

每个辅助索引页在Insert Buffer BitMap中,占用4位(bit),由下表三部分组成

名称 大小 说明
IBF_BITMAP_FREE 2 表示该辅助索引页可用空间数量,可取值为
0表示无可用空间
1表示剩余空间 > 1/32 页(512字节)
2表示剩余空间 > 1/16 页
3表示剩余空间 > ⅛ 页
IBF_BITMAP_BUFFERED 1 1表示该辅助索引页有记录被缓存在Insert Buffer B+树中
IBF_BITMAP_IBUF 1 1表示该页为Insert Buffer B+树的索引页

相关知识点:

  • 为什么一个页中,至少要有 1/32 的剩余空间? 按照一般情况来说,一页为16K,16 * 1024 / 32 = 512 B。从磁盘的物理结构来看存取信息的最小单位是扇区,一个扇区大小为 512 B.

Merge Insert Buffer —— 以一定的频率刷新到磁盘(将Insert Buffer和辅助索引页字节点进行merge操作)

merge insert buffer 的操作可能会发生在以下几种情况:

  1. 辅助索引页被读取到缓冲池时。例如执行正常的SELECT 操作,这时需要检查Insert BufferBitMap页,确认该辅助索引页是否有记录存放于Insert Buffer B+树中,若有,则将Insert Buffer B+树中的记录插入到该辅助索引页中。
  2. insert buffer bitmap追踪到该辅助索引页已无可用空间时(启用insert buffer后,辅助索引页中的记录可能被插入到insert buffer B+树中,为了保证每次的merge insert buffer页成功,通过insert buffer bitmap类型的特殊页来记录辅助索引页的可用空间),并至少得由**1/32** 的可用空间。若往辅助索引页插入一条记录后,发现空间少于 1/32 ,则会强制读取辅助索引页,将Insert Buffer B+树中,该页的记录插入到辅助索引页中。 (为什么是 1/32?)
  3. master thread:此线程中每秒或者每10秒进行一次merge insert buffer操作,不同之处在于每次merge的数量不一样。

Double Write - 两次写

Doublewrite架构

Doublewrite Buffer被MySQL放在官方文档目录的 "InnoDB On-Disk Structures" 中了,但真实情况是Doublewrite Buffer是内存+磁盘的结构。

背景

操作系统可以看成是一个程序,作为程序而言,都有最小处理单位的说法,我们常见的服务器一般都是Linux操作系统,对应文件系统的页(OS Page)就可以看成是Linux操作系统与文件系统交互的最小单位。

一般情况下,除了操作系统的页(OS Page)为4KB之外,其余程序的页(Page)都会大于等于操作系统的页大小,比如,Oracle的Page大小为8KB。

MySQL的Page大小也可以通过上面innodb_page_size参数指定,具体情况如下:

  • <5.6版本时,不可调整;
  • 5.6版本时,可自定义为8KB、4KB,但不能调大【调小是为了调高数据对齐概率】;
  • 5.7以上,才可以改成32KB、64KB【且最好设置成默认值16KB的整数倍,这是全局选项,无法在MySQL运行过程中动态修改】。

说了这么多,其实大多数情况一般都不用修改,使用默认值即可。

有点扯远了。我们知道操作系统的页大小和MySQL的页大小了,而且MySQL程序是跑在Linux操作系统上的,所以可以得出如下结论:

MySQL将Buffer Pool中一页数据刷入磁盘,要写4个文件系统里的页(也可以说成一个MySQL数据页映射4个系统页)

Doublewrite解决了什么问题?

为了解决**部分页面写入**问题(Partial Page Write)。

当前所有数据库普遍采用 Write Ahead Log 策略,即先写日志在修改磁盘数据,这样可以保证内存中丢失的数据可以通过Log恢复。

而日志记录的内容是以数据页为单位的(即数据库最小操作单元)

在PostgreSQL中默认page大小为8KB,在MySQL中默认page大小为16KB,而操作系统的单次IO一般以4KB为单位进行读写,所以写完WAL日志(or redo log)后,在往磁盘写数据页的过程中如果一个page写到一半出现了问题,那么下次启动在根据 WAL 日志恢复时,再基于这个corrupted page进行恢复肯定是不行的。

一个数据页的大小是16K,假设在把内存中的脏页写到数据库的时候,写了2K突然掉电,也就是说前2K数据是新的,后14K是旧的,那么磁盘数据库这个数据页就是不完整的,是一个坏掉的数据页。redo只能加上旧、校检完整的数据页恢复一个脏块,不能修复坏掉的数据页,所以这个数据就丢失了,可能会造成数据不一致。

Doublewrite是指哪两次写入?

  1. 写Doublewrite buffer,注意: Doublewrite buffer**是磁盘不是内存**。
  2. 写入数据文件。

写入顺序:先写doublewrite buffer,写**成功后**再写到数据文件。

Doublewrite buffer存储区位于什么地方?

# Doubliewrite文件命名
+#  #_页大小_文件编号.dblwr
+# 页大小一般默认就是16KB,由innodb_page_size控制
+-rwxrwxrwx 1 root root 192K  7月  6 23:15 '#ib_16384_0.dblwr'
+-rwxrwxrwx 1 root root 8.2M  7月  6 23:16 '#ib_16384_1.dblwr'
+
# 控制是否启用双写缓冲区,默认启用。生产环境禁止关闭
+innodb_doublewrite=on
+
+
+# 指定doublewrite文件的存储路径,默认是跟innodb_data_home_dir一起,一般就是数据目录datadir
+innodb_doublewrite_dir=innodb_data_home_dir
+
+# 指定doublewrite文件的数量,默认为每个buffer_pool_instance创建2个dbw文件,最小是2,最大是256
+innodb_doublewrite_files=
+
+# innodb_doublewrite_pages 变量(在 MySQL 8.0.20 中引入)控制每个线程的最大双写页数。如果未指定值,将设置为 innodb_write_io_threads 值。该变量用于高级性能调整。默认值适合大多数用户。
+innodb_doublewrite_pages=innodb_write_io_threads
+

innodb 什么时候将脏页写入Doublewrite buffer中?

由以下几个参数决定:

  • innodb_max_dirty_pages_pct_lwm: 低位水平标记,达到该值将启动缓冲刷新,默认为10(百分比,脏页/缓冲池)
  • innodb_max_dirty_pages_pct: 脏页数量与缓冲池比例阈值,默认为90
  • 如果开启自适应刷新(Adaptive Flushing)InnoDB根据重做日志生成的速度和当前的刷新率,使用自适应刷新算法来动态调整刷新率。

如何查看双写缓冲区的大小?

8.0.20之前,Doublewrite buffer是系统表空间中连续的128个页(每个页16k),总共2M

Doublewrite buffer从脏页刷新速度由哪些参数控制?

清洗线程数由innodb_page_cleaners配置(8.0.20版本默认为4),单个线程最多每次写入doublewrite buffer 的页面数由innodb_doublewrite_pages变量控制, 在默认情况下为innodb_write_io_threads的值(默认4), 每秒写入次数还受到innodb_io_capacity (定义了每秒I / O操作数,默认值200)和innodb_io_capacity_max(InnoDB在由后台任务每秒执行的最大IOPS数 )控制。

为什么有了redo log,还需要doublewrite buffer?

一般都认为,redo提供了崩溃恢复功能,

两次写带给innodb存储引擎的是数据页的可靠性

使用两次写的原因:

当innodb存储引擎正在写入某个页到表中,而这个页只写了一部分就发生了宕机,称为部分写失效,会导致数据丢失,可以通过重做日志恢复,可是重做日志中记录的是对页的物理操作,如偏移量80,写ddd操作。

如果这个页本身已经损坏,则重做也没意义,因此,可以在应用重做之前,用户需要一个页的副本,当发生写失效时,通过副本还原该页,再进行重做,这就是doublewrite。

在数据库中,数据是被分成一块一块的。在操作系统中,数据也是被分成一块一块的。 一般情况下,数据库的块要比操作系统的快大,且数据库块的大小是操作系统块的大小的整数倍。 所以,数据库的块没法保证原子地持久化。

Double write 要解决的是 inplace update 的 partial write 的问题。什么叫 partial write?数据库 flush 脏页的时候,系统可能宕机,这个时候,数据库的一个脏页可能只刷了一部分。 而 InnoDB 的 redo log 没有记录整个 page 的内容。因为如果每次修改都记录整个 page,那日志就太大了。 也就是说, old_page + redo_log => new_page。如果 old_page 的内容被写坏了,数据就没法恢复了。 Double write 的做法就是先将 old_page + redo_log 得到的 new_page 先持久化到磁盘上的“另一个地方”。然后再进行 inplace update,如果中途发生宕机,可以从“另一个地方”恢复这个 page 的数据。


partial write的问题是很多数据库设计中都需要考虑到这么一个临界点的问题。MySQL中的页是16k,数据的校验是按照这个为单位进行的,而操作系统层面的数据单位肯定达不到16k(比如是4k),那么一旦发生断电时,只保留了部分写入,如果是Oracle DBA一般对此都会很淡定,说用redo来恢复嘛。但可能我们被屏蔽了一些细节,MySQL在恢复的过程中一个基准是检查page的checksum,也就是page的最后事务号,发生这种partial page write 的问题时,因为page已经损坏,所以就无法定位到page中的事务号,这个时候redo就无法直接恢复。

doublewrite的构成:

一部分是内存的doublewrite buffer,大小为2MB,另一部分是物理磁盘上共享空间中的连续128页,两个区,大小也为2MB。其中120个用于批量刷脏数据,另外8个用于Single Page Flush。根据阿里翟卫祥同学分析,之所以这样做是因为批量刷脏是后台线程做的,这样不影响前台线程。而Single Page Flush是用户线程发起的,需要尽快地刷脏并替换出一个空闲页出来。所以不是一个严格的64+64的拆分,最后也给出了这篇文章的链接。(https://yq.aliyun.com/articles/50627

2MB的 Double Write 区,可以存储 2 * 1024 / 16 = 128 页

doublewrite的应用:

1.1.1.Double Write图示

  1. 当一系列机制触发数据缓冲池中的脏页刷新时,并不直接写入磁盘数据文件中,而是先拷贝至内存中的doublewrite buffer中;
  2. 接着从doublewrite buffer分两次写入磁盘共享表空间中(连续存储,顺序写,性能很高),每次写1MB;
  3. 待第二步完成后,再将doublewrite buffer中的脏页数据写入实际的各个表空间文件(离散写);(脏页数据固化后,即进行标记对应doublewrite数据可覆盖)。

因此在操作系统将页写入磁盘的过程中发生了崩溃,innodb可以从共享表空间中的doublewrite中找到该页的副本,将其复制到表空间文件,再应用重做日志恢复

1.1.1.0.Double Write整体图

doublewrite的设置:

参数skip_innodb_doublewrite可以禁止使用两次写功能,这时可能会发生写失效问题,

有些文件系统本身就提供了部分写失效的防范机制,比如ZFS文件系统,因此用户就可以不必启用两次写功能。

doublewrite的崩溃恢复

如果操作系统在将页写入磁盘的过程中发生崩溃,在恢复过程中,innodb存储引擎可以从共享表空间的doublewrite中找到该页的一个最近的副本,将其复制到表空间文件,再应用redo log,就完成了恢复过程。

对于文件校验来说,一个中心词就是checksum。如果出现了partial write的时候,比如断电,那么两次写的过程中,很可能page是不一致的,这样checksum校验就很可能出现问题。而出现问题时,因为有了前期写入共享表空间的页信息,所以就可以重构出页的信息重新写入。

因为有副本所以也不担心表空间中数据页是否损坏。

doublewrite的副作用

  1. double write带来的写负载 double write是一个buffer, 但其实它是开在物理文件上的一个buffer, 其实也就是file, 所以它会导致系统有更多的fsync操作, 而硬盘的fsync性能是很慢的, 所以它会降低mysql的整体性能。

但是,doublewrite buffer写入磁盘共享表空间这个过程是连续存储,是顺序写,性能非常高,(约占写的%10),牺牲一点写性能来保证数据页的完整还是很有必要的。

  1. 监控double write工作负载
mysql> show global status like '%dblwr%';
++----------------------------+-------+
+| Variable_name              | Value |
++----------------------------+-------+
+| Innodb_dblwr_pages_written | 7     |
+| Innodb_dblwr_writes        | 3     |
++----------------------------+-------+
+2 rows in set (0.00 sec)
+

关注点:Innodb_dblwr_pages_written / Innodb_dblwr_writes

开启doublewrite后,每次脏页刷新必须要先写doublewrite,而doublewrite存在于磁盘上的是两个连续的区,每个区由连续的页组成,一般情况下一个区最多有**64个页**,所以一次IO写入应该可以最多写64个页。

而根据以上系统Innodb_dblwr_pages_written与Innodb_dblwr_writes的比例来看,大概在3左右,远远还没到64(如果约等于64,那么说明系统的写压力非常大,有大量的脏页要往磁盘上写),所以从这个角度也可以看出,系统写入压力并不高。

  1. 关闭double write适合的场景

  2. 海量DML

  3. 不惧怕数据损坏和丢失

  4. 系统写负载成为主要负载

    mysql> show variables like '%double%';
    ++--------------------+-------+
    +| Variable_name      | Value |
    ++--------------------+-------+
    +| innodb_doublewrite | ON    |
    ++--------------------+-------+
    +1 row in set (0.04 sec)
    +

作为InnoDB的一个关键特性,doublewrite功能默认是开启的,但是在上述特殊的一些场景也可以视情况关闭,来提高数据库写性能。静态参数,配置文件修改,重启数据库。

为什么没有把double write里面的数据写到data page里面呢?

  1. double write里面的数据是连续的,如果直接写到data page里面,而data page的页又是离散的,写入会很慢。
  2. double write里面的数据没有办法被及时的覆盖掉,导致double write的压力很大;短时间内可能会出现double write溢出的情况,所以大量的DML操作时,可以关闭Double Write,但会写失效

还可参考这篇文章:页断裂(partial write)与doublewrite技术

https://www.modb.pro/db/114783


Adaptive Hash Index - 自适应哈希索引

含义

innodb存储引擎会监控对表上的各索引页的查询,如果观察到建立哈希索引可以带来速度提升,则建立哈希索引,称之为自适应哈希索引(Adaptive hash index,AHI).AHI是通过**缓冲池的B+树**构造而来,因此建立速度很快,innodb会自动根据访问的频率和模式自动的为某些热点页建立哈希索引。

建立要求:

  1. 对于这个页的连续访问模式必须是一样的,即指查询条件一样(对于多列索引来说,where a =1 和 where a = 1 and b =2时不同的访问模式)
  2. 以该模式访问了100次
  3. 页通过该模式访问了N次,其中N=页中记录*1/16

限制:

哈希索引只能用来搜索等值的查询,如select * from table where index_col="xxx";而对于其他查找类型,如范围查找不能使用哈希索引,通过参数innodb_adptive_hash_index来禁止或者开启此特性,默认AHI为开启状态。


AIO - 异步IO

目的:

为了提高磁盘操作性能,使用异步io(asynchronous io,AIO)的方式来处理磁盘操作,

含义:

与AIO相对应的是Sync IO,即每次进行一次IO操作,需要等待此次操作结束后,才能进行下一步操作。

用户可以在发出一个io请求后立即再发出另一个io请求,当全部io请求发送完毕后,等待所有的io操作完成,称为AIO,AIO还可以进行io merge操作,也就是将多个io合并为一个io.这样可以提高IPOS性能。

例如,用户查询的页为

(3,5)(3,6)(3,7)

每个页的大小为16KB,那么,Sync IO需要三次IO 操作。而AIO会判断这三个页是连续的,(通过space,offset就可以看出来)因此,AIO会发出一个IO请求,从(3,5)开始,读取48KB的页

实现:

​ 在innodb1.1.x之前,Aio是通过innodb存储引擎的代码模拟实现,而从innodb1.1.x开始,提供了内核级别AIO的支持,即为native AIO,

应用

read ahead方式的读取,脏页的刷新等都是AIO完成的


刷新临近页

工作原理

当刷新一个脏页时,innodb会检测**该页所在区的所有页**,如果是脏页,那么一起刷新

好处

通过AIO,将多个IO写入操作合并到一个IO操作中,对于机械硬盘来说,性能提升很明显。固态硬盘建议关闭

参数控制

参数innodb_flush_neighbors来开启或关闭该特性,为0则关闭


预读 - read ahead

InnoDB在I/O的优化上有个比较重要的特性为预读,预读请求是一个i/o请求,它会异步地在缓冲池中预先回迁多个页面,预计很快就会需要这些页面,这些请求在一个范围内引入所有页面。InnoDB以64个page为一个extent,那么InnoDB的预读是以page为单位还是以extent?

1.1.1.2.预读

数据库请求数据的时候,会将读请求交给文件系统,放入请求队列中;相关进程从请求队列中将读请求取出,根据需求到相关数据区(内存、磁盘)读取数据;取出的数据,放入响应队列中,最后数据库就会从响应队列中将数据取走,完成一次数据读操作过程。

接着进程继续处理请求队列,(如果数据库是全表扫描的话,数据读请求将会占满请求队列),判断后面几个数据读请求的数据是否相邻,再根据自身系统IO带宽处理量,进行预读,进行读请求的合并处理,一次性读取多块数据放入响应队列中,再被数据库取走。(如此,一次物理读操作,实现多页数据读取,rrqm>0(# iostat -x),假设是4个读请求合并,则rrqm参数显示的就是4)

预读请求是一个I/O请求,用于异步预取缓冲池中的多个页面,以预期这些页面将很快被需要。请求在一个区段中引入所有页面。InnoDB使用两种预读算法来提高I/O性能:

  • 线性预读(默认方式),它根据按顺序访问的缓冲池中的页面来预测可能很快需要哪些页面。通过使用配置参数innodb_read_ahead_threshold来调整触发异步读取请求所需的顺序页面访问次数,可以控制InnoDB何时执行预读操作。在添加这个参数之前,InnoDB只会在读取当前区段的最后一页时计算是否要对整个下一个区段发出异步预取请求。

    配置参数innodb_read_ahead_threshold控制着InnoDB检测顺序页面访问模式的敏感度。如果从一个区段连续读取的页面数大于或等于innodb_read_ahead_threshold, InnoDB会对整个后续区段启动一个异步预读操作。innodb_read_ahead_threshold可以设置为0-64之间的任何值。默认值是56。值越高,访问模式检查越严格。例如,如果你将这个值设置为48,InnoDB只会在48个页面被顺序访问的情况下触发一个线性提前读取请求。如果值是8,InnoDB会触发异步提前读取,即使只有8个页面被顺序访问。您可以在MySQL配置文件中设置该参数的值,或者使用set GLOBAL语句动态地更改它,这需要足够的权限来设置全局系统变量。

mysql> show variables like 'innodb_read_ahead_threshold';
++-----------------------------+-------+
+| Variable_name               | Value |
++-----------------------------+-------+
+| innodb_read_ahead_threshold | 56    |
++-----------------------------+-------+
+
  • 随机预读,它根据缓冲池中已经存在的页面预测何时可能需要这些页面,而不考虑这些页面被读取的顺序。如果在缓冲池中发现来自相同区段的13个连续的页面,InnoDB异步地发出请求来预取区段的剩余页面。默认是关闭的。要启用这个特性,请将配置变量innodb_random_read_ahead设置为ON。
mysql> show variables like 'innodb_random_read_ahead';
++--------------------------+-------+
+| Variable_name            | Value |
++--------------------------+-------+
+| innodb_random_read_ahead | OFF   |
++--------------------------+-------+
+

SHOW ENGINE INNODB STATUS命令显示统计信息,帮助您评估预读算法的有效性。统计信息包括以下全局状态变量的计数器信息:

  • Innodb_buffer_pool_read_ahead
  • Innodb_buffer_pool_read_ahead_evicted
  • Innodb_buffer_pool_read_ahead_rnd

在微调innodb_random_read_ahead设置时,这些信息可能很有用。

以上内容来源于

  1. 官方文档
  2. InnoD存储引擎的关键特性
  3. 阿里云栖社区-Double Write
\ No newline at end of file diff --git "a/advanced/4.InnoDB\345\206\205\345\255\230\347\273\223\346\236\204\342\200\224\342\200\224\347\274\223\345\206\262\346\261\240/index.html" "b/advanced/4.InnoDB\345\206\205\345\255\230\347\273\223\346\236\204\342\200\224\342\200\224\347\274\223\345\206\262\346\261\240/index.html" new file mode 100644 index 000000000..5aa4b18fa --- /dev/null +++ "b/advanced/4.InnoDB\345\206\205\345\255\230\347\273\223\346\236\204\342\200\224\342\200\224\347\274\223\345\206\262\346\261\240/index.html" @@ -0,0 +1,299 @@ + 4.InnoDB内存结构——缓冲池 - MySQL WIKI
跳转至

缓冲池

传统的 DBMS 架构都属于 disk-oriented architecture,即假设数据主要存储在非易失的磁盘(non-volatile disk)上。

于是 DBMS 中一般都有磁盘管理模块(disk manager),它主要负责数据在非易失与易失(volatile)的存储器之间的移动。

这里需要理解两点:

  • 为什么需要将数据在不同的存储器之间移动?
  • 为什么要自己来做数据移动的管理,而非利用 OS 自带的磁盘管理模块?

从 InnoDB 逻辑存储结构来看,所有的数据都被逻辑的存放在一个空间中,这个空间就叫做表空间(tablespace)。

****表空间由 段(segment)、区(extent)、页(page)****组成。

当我们创建一个表之后,在磁盘上会有对应的表名称.ibd的磁盘文件。

表空间的磁盘文件里面有很多的数据页,一个数据页最多 16kb,因为不可能一个数据页一个磁盘文件,所以数据区的概念引入了。

内存

内存缓冲池(buffer pool)

InnoDB 存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进管理,因此可以将其视为基于磁盘的数据库系统。

在数据库系统中,由于 CPU 和磁盘交换速度的差距,基于磁盘的数据库系统通常使用缓冲池技术来提高数据库的整体性能。

内存缓冲池简单说就是 MySQL 进程向操作系统申请一块内存区域,通过内存的速度来弥补磁盘的速度,在数据库中读取页时,首先将磁盘读到的页放到缓冲池中,这个过程称为将页 fix 到缓冲池。

buffer pool 一般称为内存缓冲池,在 MySQL 也有人简称bp

下次再读取相关的页时,下次再读取相同的页时,先判断是否在缓冲池中,若在,则称为该页在缓冲池被命中。

对于修改数据(增删改),同样首先修改缓冲池中的页,然后在以一定的频率刷新到磁盘。通过一种被称作 checkpoint 的机制刷回磁盘。

关键概念——数据页

在 MySQL 中,innodb 表以 tablename.ibd 格式的文件存放在磁盘中。

数据页是 MySQL 抽象出来的数据单位,磁盘文件中就是存放了很多数据页,每个数据页里存放了很多行数据。默认情况下,数据页的大小是 16KB。

数据库采用数据页的形式组织数据。MySQL 默认的非压缩数据页为 16KB。在 ibd 中间中,0-16KB 偏移量即为 0 号数据页,16KB-32KB 的为 1 号数据页,依次类推。

数据页的头尾除了一些元信息外,还有 Checksum 校验值,这些校验值在写入磁盘前计算得到,当从磁盘中读取时,重新计算校验值并与数据页中存储的对比,如果发现不同,则会导致 MySQL crash。遇到这种情况,往往需要从备份集中恢复数据,如果备份不可用,只能使用 innodb_force_recovery 强行启动,然后尽可能多的导出数据。

严格来讲,InnoDB 的数据页有很多种,比如,索引页,Undo 页,Inode 页,系统页,BloB 页等,一共有 10 多种。

对应的,在 Buffer Pool 中,也是以数据页为数据单位,存放着很多数据。

但是通常被叫做缓存页,因为 Buffer Pool 是一个缓冲池,并且里面的数据都是从磁盘文件中缓存到内存中。

所以,默认情况下缓存页的大小也是 16KB,因为它和磁盘文件中数据页是一一对应的。

缓冲池和磁盘之间的数据交换的单位是数据页,包括从磁盘中读取数据到缓冲池和缓冲池中数据刷回磁盘中。

缓冲池中基本概念

缓冲池是 MySQL 向操作系统申请的一块内存区域,操作系统是以页为单位对内存进行管理。

缓冲池是 InnoDB 存储引擎中最重要的组件。为了提高 MySQL 的并发性能,使用到的数据都会缓存在缓冲池中,然后所有的增删改查操作都将在缓冲池中执行。

对于每个更新请求,尽量就是 只更新内存,然后往磁盘顺序写日志文件

更新内存的性能是极高的,然后顺序写磁盘上的日志文件的性能也是比较高的,因为顺序写磁盘文件,他的性能要远高于随机读写磁盘文件。

具体来看,缓冲池中的页类型有:

  • 数据页
  • 索引页
  • undo 页
  • 插入缓冲
  • 自适应哈希索引
  • InnoDB 存储的锁信息
  • 数据字典信息等。

不能简单的认为,缓冲池只是缓冲索引和数据页。它们只是占内存缓冲池很大的一部分而已

在 Linux 中,操作系统以页为单位管理内存,无论是将磁盘中的数据加载到内存中,还是将内存中的数据写回磁盘,操作系统都会以页面为单位进行操作。 哪怕我们只向磁盘中写入一个字节的数据,我们也需要将整个页面中的全部数据刷入磁盘中。 在操作系统层面,每个进程都有自己独立的地址空间,看到的都是操作系统虚拟出来的地址空间,虚拟地址最终还是要落在实际内存的物理地址上进行操作的。 操作系统就会通过页表的机制来实现进程的虚拟地址到物理地址。其中每一页的大小都是固定的。

####X86:
+[root@ens8 ~]# getconf PAGESIZE
+4096
+####ARM:
+root@ens8ARM:~# getconf PAGESIZE
+65536
+

Linux 同时支持正常大小的内存页和大内存页(Huge Page)

绝大多数处理器上的内存页(page)的默认大小都是 4KB,虽然部分处理器会使用 8KB、16KB 或者 64KB 作为默认的页面大小,但是 4KB 的页面仍然是操作系统默认内存页配置的主流;

为什么 Linux 默认页大小是 4KB

总结一下:

  • innodb_page_size 作为 innodb 和 OS 交互单位。文件系统对文件的 buffer IO,也是 page 为单位进行处理的。

  • InnoDB 缓冲池中的数据访问是以 Page 为单位的,每个 Page 的大小默认为 16KB,Buffer Pool 是用来管理和缓存这些 Page 的。

内存缓冲池相关参数配置

# 内存缓冲池总大小,默认是128M,应当适当设置调大buffer_pool_size,一般设置为服务器内存60%。通常实际占用的内存会比配置的还要大10%
+# MySQL5.7.5之后可以动态在线调整。在调整innodb_buffer_pool_size 期间,用户的请求将会阻塞,直到调整完毕,所以请勿在白天调整,尽量在凌晨3-4点业务低峰期调整。
+innodb_buffer_pool_size=8G
+
+# 内存缓冲池实例数,默认是1,通过将buffer pool分成多个区,每个区用独立的锁保护,这样就减少了访问buffer_pool时需要上锁的粒度,以提高并发能力和性能。
+innodb_buffer_pool_instances=16
+
+# innodb页大小,默认是16KB,一般设置为16KB或64KB
+innodb_page_size=16KB
+
+# 在调整内存缓冲池总大小时,内部把数据页移动到一个新的位置,单位是块。如果想增加移动的速度,需要调整innodb_buffer_pool_chunk_size参数的大小,默认是128M。缓冲池配置时的基本单位,以块的形式配置,innodb_buffer_pool_chunk_size参数指明块大小。
+# innodb_buffer_pool_size=innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances * n
+innodb_buffer_pool_chunk_size=128MB
+

什么是 LRU 算法

LRU 就是一种很常见的缓存淘汰策略。按照英文的直接原义就是 Least Recently Used,最近最久未使用。

利用好 LRU 算法,我们能够提高对热点数据的缓存效率,进而提升缓存服务的内存使用率。

一般计算机内存容量有限,操作系统分配给 MySQL 的内存缓存池容量自然也有限,如果缓存池满了就要删除一些内容,给新内容腾位置。

但问题是,删除哪些内容呢?我们肯定希望删掉哪些没什么用的缓存,而把有用的数据继续留在缓存里,方便之后继续使用。

那么,什么样的数据,我们判定为「有用的」的数据呢?LRU 缓存淘汰算法就是一种常用策略。

LRU 的全称是 Least Recently Used,这个算法认为最近使用过的数据应该是是「有用的」,很久都没用过的数据应该是无用的,内存满了就优先删那些很久没用过的数据。

它是按照一个非常著名的计算机操作系统基础理论得来的:

最近使用的页面数据会在未来一段时期内仍然被使用,已经很久没有使用的页面很有可能在未来较长的一段时间内仍然不会被使用

基于这个思想,会存在一种缓存淘汰机制,每次从内存中找到 最久未使用的数据然后置换出来 ,从而存入新的数据!

LRU 的主要衡量指标是 使用的时间 。附加指标是 使用的次数

在计算机中大量使用了这个机制,它的合理性在于 优先筛选热点数据 ,所谓热点数据,就是 最近最多使用的数据

LRU 的实现

  • 实现 LRU 缓存的常用方法是使用固定长度的队列。实现 LRU 的关键是将所有最近使用的数据放在队列的开头。
  • 每次有新数据插入之前,我们检查队列是否已满。如果队列已满,我们将删除其最后一个元素,并将新节点插入队列的开头。
  • 如果队列未满,我们只需将数据添加到队列的开头。

在一些大厂面试中,经常会要求手写 LRU 算法。

LRU List

通常,数据库中的缓冲池是通过 LRU (Latest Recent Used) 算法来管理的,即最频繁使用的页在 LRU 最前端。

但是 MySQL InnoDB 对传统的 LRU 算法做了一些优化。在 buffer pool 中的的数据页可以认为是一个 LIST 列表,分为两个子列表 (New Sublist) ( Old Sublist)

# 这个参数控制着 New Sublist 和 Old Sublist 的比例 ,New Sublist占5/8,Old Sublist占3/8
+innodb_old_blocks_pct=37
+

可以简单理解为 New Sublist 中的页都是最活跃的热点数据页。

当有数据页要加载到内存中,就插入到 Old Sublist 的头部,并且从 Old Sublist 尾部移除不再使用的页。

可以看到,这是一个先进先出的队列。

很明显不用一个队列来管理这些,可以避免一次大表的全表扫描,就把缓冲池中的所有数据都刷出。

MySQL 默认在 InnoDB 缓冲池(而不是整个缓冲池)中仅保留最频繁访问页的 25% 。

在多数使用场景下,合理的选择是:保留最有用的数据页,比加载所有的页(很多页可能在后续的工作中并没有访问到)在缓冲池中要更快。

# INFORMATION_SCHEMA中有几个缓冲池表提供有关InnoDB缓冲池中页面的缓冲池状态信息和元数据。
+
+############ 查询INNODB_BUFFER_PAGE表可能会影响性能。 除非您了解性能影响并确定其可接受,否则请勿在生产系统上查询此表。 为避免影响生产系统的性能,请重现要调查的问题并在测试实例上查询缓冲池统计信息。
+
+mysql> SHOW TABLES FROM INFORMATION_SCHEMA LIKE 'INNODB_BUFFER%';
++-----------------------------------------------+
+| Tables_in_INFORMATION_SCHEMA (INNODB_BUFFER%) |
++-----------------------------------------------+
+| INNODB_BUFFER_PAGE_LRU                        |
+| INNODB_BUFFER_PAGE                            |
+| INNODB_BUFFER_POOL_STATS                      |
++-----------------------------------------------+
+
+# INNODB_BUFFER_PAGE:保存InnoDB缓冲池中每个页面的信息。
+
+# INNODB_BUFFER_PAGE_LRU:保存有关InnoDB缓冲池中页面的信息,特别是它们在LRU列表中的排序方式,确定哪些页面在缓冲池变满时从缓冲池中逐出。 INNODB_BUFFER_PAGE_LRU表与INNODB_BUFFER_PAGE表具有相同的列。
+# 但INNODB_BUFFER_PAGE_LRU表具有LRU_POSITION列而不是BLOCK_ID列。
+# INNODB_BUFFER_POOL_STATS:提供缓冲池状态信息。许多相同的信息由SHOW ENGINE INNODB STATUS输出提供,或者可以使用InnoDB缓冲池服务器状态变量获得。
+

使用 InnoDB 标准监视器监视缓冲池

InnoDB可以使用访问的标准监视器输出, SHOW ENGINE INNODB STATUS提供有关缓冲池操作的度量。

缓冲池度量标准位于BUFFER POOL AND MEMORYInnoDB标准监视器”输出中的部分,其输出类似于以下内容:

----------------------
+BUFFER POOL AND MEMORY
+----------------------
+Total large memory allocated 2198863872
+Dictionary memory allocated 776332
+Buffer pool size   131072
+Free buffers       124908
+Database pages     5720
+Old database pages 2071
+Modified db pages  910
+Pending reads 0
+Pending writes: LRU 0, flush list 0, single page 0
+Pages made young 4, not young 0
+0.10 youngs/s, 0.00 non-youngs/s
+Pages read 197, created 5523, written 5060
+0.00 reads/s, 190.89 creates/s, 244.94 writes/s
+Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not
+0 / 1000
+Pages read ahead 0.00/s, evicted without access 0.00/s, Random read
+ahead 0.00/s
+LRU len: 5720, unzip_LRU len: 0
+I/O sum[0]:cur[0], unzip sum[0]:cur[0]
+

下表描述了InnoDB标准监视器报告的缓冲池度量标准

名称 描述
Total memory allocated 为缓冲池分配的总内存(以字节为单位)
Dictionary memory allocated InnoDB数据字典分配的总内存,以字节为单位
Buffer pool size 分配给缓冲池的页面总大小
Free buffers 缓冲池空闲列表的页面总大小
Database pages 缓冲池 LRU 列表的页面总大小
Old database pages 缓冲池旧 LRU 子列表的页面总大小
Modified db pages 缓冲池中当前修改的页面数
Pending reads 等待读入缓冲池的缓冲池页面数
Pending writes LRU 从 LRU 列表的底部开始写入的缓冲池中的旧脏页数
Pending writes flush list 检查点期间要刷新的缓冲池页面数
Pending writes single page 缓冲池中暂挂的独立页面写入数
Pages made young 缓冲池 LRU 列表中变年轻的页面总数(已移至“ new ”页面的子列表的开头)
Pages made not young 缓冲池 LRU 列表中没有年轻的页面总数(保留在“ old ”子列表中但没有年轻的页面)
youngs/s 每秒平均访问缓冲池 LRU 列表中的旧页面所导致的页面年轻
non-youngs/s 每秒平均访问缓冲池 LRU 列表中的旧页面导致的页面不年轻
Pages read 从缓冲池读取的页面总数
Pages created 在缓冲池中创建的页面总数
Pages written 从缓冲池写入的页面总数
reads/s 每秒平均每秒读取的缓冲池页面数
creates/s 每秒平均创建的缓冲池页面的每秒数量
writes/s 每秒平均缓冲池页面写入数
Buffer pool hit rate 从缓冲池内存与磁盘存储读取的页面的缓冲池页面命中率
young-making rate 页面访问的平均命中率使页面更年轻
not (young-making rate) 页面访问未使页面变年轻的平均命中率
Pages read ahead 预读操作的每秒平均数
Pages evicted without access 每秒从缓冲池访问而未访问的页面的平均值
Random read ahead 随机预读操作的每秒平均数
LRU len 缓冲池 LRU 列表的页面总大小
unzip_LRU len 缓冲池 unzip_LRU 列表的页面总大小
I/O sum 最近 50 秒内访问的缓冲池 LRU 列表页面的总数
I/O cur 已访问的缓冲池 LRU 列表页面的总数
I/O unzip sum 已访问的缓冲池 unzip_LRU 列表页面的总数
I/O unzip cur 已访问的缓冲池 unzip_LRU 列表页面的总数

缓冲池预热

https://dev.mysql.com/doc/refman/8.0/en/innodb-preload-buffer-pool.html

在生产中,重启 MySQL 后,会发现一段时间内 SQL 性能变差,然后最终恢复到原有性能。

MySQL 进程重启后,它在内存中的数据自然就释放了。通过业务的访问才会逐步将热点数据从磁盘缓存到 InnoDB Buffer Pool 中,从磁盘读取数据自然没有从内存读取数据快。随着业务的访问,MySQL 会逐步经常操作的热点数据都已经缓存到 InnoDB Buffer Pool 缓冲池中。最后趋于原有情况。

MySQL 重启后,将热点数据从磁盘逐渐缓存到 InnoDB Buffer Pool 的过程称为预热(官方文档称之为 warmup)。

让应用系统自身慢慢通过 SQL 给InnoDB Buffer Pool预热成本很高,如果遇到高峰期极有可能带来一场性能灾难,导致业务卡顿不能顺利运营。

为了避免这种情况发生,MySQL 5.6 引入了数据预热机制,在停止数据库的时候,把内存中的热点数据 dump 到磁盘文件中,启动时,直接把热点数据从磁盘加载回内存中。

需要注意的是,对于较大内存的数据库来说,配置这种预热机制,会让关闭数据库的时间非常长。同样启动过程也会延长。

# 关闭数据库时是否保留当前的缓冲池的状态到磁盘中,MySQL5.7之后默认开启
+innodb_buffer_pool_dump_at_shutdown=on
+
+# 保留内存缓冲池中数据的比例,默认是25%
+innodb_buffer_pool_dump_pct=25
+
+# 缓冲池数据dump到磁盘中的文件名称,默认是ib_buffer_pool,一般放在磁盘的datadir/ib_buffer_pool中
+innodb_buffer_pool_filename=ib_buffer_pool
+
+# 预热开关:启动时自动从磁盘文件读取热点数据到内存的innodb_buffer_pool中
+innodb_buffer_pool_load_at_startup=on
+

MySQL 线程

刷脏

脏页

当修改数据的事务提交后,数据刷到磁盘之前,此时内存中的数据页和磁盘中的数据是不一致的,我们把此时内存中的这些数据页成为脏页。

刷脏

刷脏,即把 buffer pool 中的内存脏页数据刷回磁盘落地。

InnoDB 会在后台执行某些任务,包括从缓冲池刷新脏页(那些已更改但尚未写入数据库文件的页)。

InnoDB 当缓冲池中脏页的百分比达到定义的低水位设置时,其实就是当缓冲池中的脏页占用比达到innodb_max_dirty_pages_pct_lwm的设定值的时候,就会自动将脏页清出 buffer pool,这是为了保证buffer pool当中脏页的占有率,也是为了防止脏页占有率超过innodb_max_dirty_pages_pct的设定值,当脏页的占有率达到了 innodb_max_dirty_pages_pct 的设定值的时候,InnoDB 就会强制刷新 buffer pool pages。

InnoDB 采用一种基于 redo log 的最近生成量和最近刷新频率的算法来决定冲洗速度,这样的算法可以保证数据库的冲洗不会影响到数据库的性能,也能保证数据库 buffer pool 中的数据的脏数据的占用比。这种自动调整刷新速率有助于避免过多的缓冲池刷新限制了普通读写请求可用的 I/O 容量,从而避免吞吐量突然下降,但还是对正常 IO 有影响。

多线程刷脏

  • 5.6 版本以前,脏页的清理工作交由 master thread 的;

  • Page cleaner thread 是 5.6.2 引入的一个新线程(单线程),从 master 线程中卸下 buffer pool 刷脏页的工作独立出来的线程(默认是启一个线程);

  • 5.7 开始支持多线程刷脏页,线程数量通过innodb_page_cleaners参数控制,默认是4个,如果数量超过 buffer pool instance,那么会降级成跟它一致。

刷脏主要分以下几种场景:

1、数据库关闭,

两阶段提交

内部 XA 事务主要指单节点实例内部,一个事务跨多个存储引擎进行读写,那么就会产生内部 XA 事务;这里需要指出的是,MySQL 内部每个事务都需要写 binlog,并且需要保证 binlog 与引擎修改的一致性,因此 binlog 是一个特殊的参与者,所以在打开 binlog 的情况下,即使事务修改只涉及一个引擎,内部也会启动 XA 事务。

外部 XA 事务与内部 XA 事务核心逻辑类似,提供给用户一套 XA 事务的操作命令,包括 XA start, XA end,XA prepre 和 XA commit 等,可以支持跨多个节点的 XA 事务。外部 XA 的协调者是用户的应用,参与者是 MySQL 节点,因此需要应用持久化协调信息,解决事务一致性问题。无论外部 XA 事务还是内部 XA 事务,存储引擎实现的 prepare 和 commit 接口都是同一条路径。

对于用户事务,是否使用二阶段提交,取决于是否开启了 binlog。

因为 MySQL 把 binlog 也看作一个存储引擎,开启 binlog,SQL 语句改变(插入、更新、删除)InnoDB 表的数据,这个 SQL 语句执行过程中,就涉及到两个存储引擎。

在 MySQL 中,两阶段提交有几个最重要的文件:

重做日志 redo log

重做日志,又叫事务日志,是 InnoDB 存储引擎层的日志。一般也叫**物理日志**

在 MySQL 里,如果每一次的更新操作都需要写进磁盘持久化,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很高。

当缓冲池中的某页数据被修改后,该页就被标记为 ”脏页“,脏页的数据会被定期刷新到磁盘上。

倘若每次一个页发生变化,就将新页的版本刷新到磁盘,那么这个开销是非常大的。并且,如果热点数据都集中在某几个页中,那么数据库的性能将变得非常差。另外,如果在从缓冲池将页的新版本刷新到磁盘时发生了宕机,那么这个数据就不能恢复了。

所以,为了避免发生数据丢失的问题,当前事务数据库系统(并非 MySQL 所独有)普遍都采用了 WAL(Write Ahead Log,预写日志)策略:即当事务提交时,先写重做日志(redo log),再修改页(先修改缓冲池,再刷新到磁盘);当由于发生宕机而导致数据丢失时,通过 redo log 来完成数据的恢复。这也是事务 ACID 中 D(Durability 持久性)的要求。

有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。

举个简单的例子,假设你非常热心且 rich 的,借出去了很多钱,但是你非常 old school,不会使用电子设备并且记性不太好,所以你用一个小本本记下了所有欠你钱的人的名字和具体金额。这样,别人还你钱的时候,你就翻出你的小本本,一页页地找到他的名字然后把这次还的钱扣除掉。

但是呢,其实你平常是非常忙碌的,没办法随时随地翻小本本做记录,因此你就想出了一个主意:每当有人还你钱的时候,你就在一张白纸上记下来,然后挑个时间对照小本本把白纸上的账目都给清了。

这就是 WAL。白纸就是 redo log,小本本就是磁盘。

所以有一种技术叫 WAL ,全称是 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘。(日志先行)

PostgreSQL 文档中有这么一句话 Write-Ahead Logging (WAL) is a standard method for ensuring data integrity

通过日志实现事务的原子性和持久性是当今的主流方案。

在磁盘中写入数据就不能像程序修改内存中变量值那样,直接改变某表某行某列的某个值,而是必须将修改数据这个操作所需的全部信息,包括修改什么数据、数据物理上位于哪个内存页和磁盘块中、从什么值改成什么值,等等,以日志的形式——即仅进行顺序追加的文件写入的形式(这是最高效的写入方式)先记录到磁盘中。只有在日志记录全部都安全落盘,数据库在日志中看到代表事务成功提交的“提交记录”(Commit Record)后,才会根据日志上的信息对真正的数据进行修改,修改完成后,再在日志中加入一条“结束记录”(End Record)表示事务已完成持久化,这种事务实现方法被称为“Commit Logging”(提交日志)。

WAL 的核心概念是数据文件(表和索引所在的位置)的更改必须仅在记录这些更改之后写入,即在描述更改的日志记录已刷新到永久存储之后。

如果我们遵循这个过程,我们不需要在每次事务提交时将数据页真正刷新到磁盘,因为我们知道在发生崩溃时我们将能够使用日志恢复数据库;

任何尚未应用的更改可以从日志记录中重做数据页。(这是前滚恢复,也称为 redo)

具体来说,当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log (磁盘中的物理文件)里面,并更新内存,这个时候更新就几乎算完成了。

(后面会讲,这时严格说还不算事务 commit 成功,客户端还看不到返回成功)

由于 redo-log 是顺序写的,所以速度比较快。redo-log 是物理日志,记录的是 “在某个数据页上做了什么修改”。

redo-log 是循环写的,当 redo-log 写完后,就要刷盘。把数据刷到磁盘中。(更严格地说,何时刷盘应该还是有参数控制的。)

由于内存缓冲的存在,对数据的增删改都先修改内存中的数据页,再定期 flush 落盘持久化。

在每次事务提交的时候,将该事务涉及修改的数据页全部刷回到磁盘中。但是这么做会有严重的性能问题,主要体现在两个方面:

  • 因为 Innodb 是以页为单位进行磁盘交互的,而一个事务很可能只修改一个数据页里面的几个字节,这个时候将完整的数据页刷到磁盘的话,太浪费资源了。
  • 一个事务可能涉及修改多个数据页,并且这些数据页在物理上并不连续,使用随机 IO 写入性能太差。

因此 MySQL 设计了 redo log 。具体来说就是只记录事务对数据页做了哪些修改,这样就能完美地解决性能问题了(相对而言文件更小,并且是顺序 IO)。

redo log 包括两部分:

  • 一个是内存中的日志缓冲(redo log buffer,内存地址空间)。(innodb_log_buffer_size)
  • 另一个是磁盘上的日志文件(redo log file,物理磁盘文件)。(innodb_log_file_size)

MySQL 每执行一条 DML 语句,先将记录写入 redo log buffer ,后续某个时间点再一次性将多个操作记录写到 redo log file 。

默认情况下,redo log 在磁盘上由名为 ib_logfile0ib_logfile1 的两个物理文件。

redolog 相关配置参数

### redolog 关键参数
+
+# redo-log 默认是在 datadir 目录下,名为 `ib_logfile1` 和 `ib_logfile2` 这样的两个文件。
+# 指定redolog的存放目录,默认是"./",即在datadir目录下,条件允许的话,一般不建议放在跟datadir同一块磁盘下,防止IO争用
+# 注意这个目录要提前创建好,并设置好正确的权限
+innodb_log_group_home_dir=/data/mysql_redo_log/
+
+# 单个redolog文件的大小,默认是48MB,最大值为512G,注意最大值指的所有redo-log文件之和
+# 如果数据库单个事务较大的话,redolog应该尽量设置的稍微大点
+innodb_log_file_size=48MB
+
+# regolog是以一组文件的形式出现。这个参数了指定了一组里面有多少个redo log文件
+innodb_log_files_in_group=2  # 默认值是2
+# regolog文件的总大小就是等于 innodb_log_file_size*innodb_log_files_in_group
+
+# redo_log_buffer 大小,默认为16M。延迟事务日志写入磁盘,把 redo log 放到该缓冲区
+# 然后根据 innodb_flush_log_at_trx_commit 参数的设置,再把日志从buffer中flush到磁盘
+# innodb_log_buffer_size是会话级的,所有整个redolog buffer占用的空间应该是innodb_log_buffer_size * connections
+# 一般默认值16MB是够用的,但如果事务之中含有blog/text等大字段,这个缓冲区会被很快填满会引起额外的IO负载。对于大事务操作。可以考虑设的大一些。
+innodb_log_buffer_size=16M
+
+## 修改redo_log文件大小必须要先关闭实例后再修改。
+## MySQL 8.0.30 版本中提供动态调整redo log 文件大小的功能。自此,可以在线调整无需重启。
+
+innodb_flush_log_at_trx_commit=1
+# innodb_flush_log_at_trx_commit 控制 redolog 从 redolog buffer刷新到磁盘的策略,具体含义如下:
+
+# 默认为1。值为1,每次 commit 都会把 redo log 从 redo log buffer 写入到 system ,并fsync刷新到磁盘文件中。
+
+# 值为2时,表示每次事务提交时 MySQL 会把日志从 redo log buffer 写入到 system ,但只写入到 file system buffer,由系统内部来 fsync 到磁盘文件。如果数据库实例 crash ,不会丢失 redo log,但是如果服务器 crash,由于 file system buffer 还来不及 fsync 到磁盘文件,所以会丢失这一部分的数据。
+
+# 值为0,表示事务提交时不进行写入redo log操作,这个操作仅在 master thread 中完成,而在 master thread 中每1秒进行一次重做日志的 fsync 操作,因此实例 crash 最多丢失1秒钟内的事务。
+
+
+# 这个参数是innodb的数据页大小单位,一般设置为
+innodb_page_size=16KB
+
+
+# https://blog.csdn.net/u010647035/article/details/104733939
+
+
+
+## MySQL8.0.30 版本中提供在线动态调整redo log文件大小的功能。自此,可以直接在线调整且无需重启
+## 自8.0.30后,redo文件总大小参数也变成了由 innodb_redo_log_capacity 参数直接控制,默认是100MB。
+## 一旦设置了这个参数,前面的组数量和大小都被忽略。
+## redo文件也存放在datadir的#innodb_redo目录中。分为两类:ordinary(已用过的,不带_tmp 后缀的) 和 spare(空闲的,带tmp后缀的) 。
+## 一般会将redo文件平均分成32份文件。以 #ib_redoN 文件命名,N为序号
+
+# 在线调整redo为8GB
+SET GLOBAL innodb_redo_log_capacity = 8589934592;
+
+# 在线直接调整redo为2GB
+set persist innodb_redo_log_capacity=2*1024*1024*1024;
+
+
+# 新增对应的状态变量innodb_redo_log_capacity_resized,方便在 MySQL 侧监控当前 REDO 日志文件大小
+show status like 'innodb_redo_log_capacity_resized';
+
+# 同时 performance_schema 库里新增表innodb_redo_log_files:获取当前使用的 REDO 日志文件 LSN 区间、实际写入大小、是否已满等统计数据。例如当前15个 REDO 日志文件的统计数据如下:一目了然!
+

redolog 动态开关

在 MySQL 8.0.21 新版本发布中,支持了一个新特性 Redo Logging 动态开关

借助这个功能,在新实例导数据的场景下,事务处理可以跳过记录 redolog 和 doublewrite buffer,从而加快数据的导入速度。

同时,付出的代价是短时间牺牲了数据库的 ACID 保障。所以主要使用场景就是向一个新实例导入数据。

注意事项

  • 该特性仅用于新实例导数据场景,不可用于线上的生产环境;
  • Redo logging 关闭状态下,支持正常流程的关闭和重启实例;但在异常宕机情况下,可能会导致丢数据和页面损坏;Redo logging 关闭后异常宕机的实例需要废弃重建,直接重启会有如下报错:[ERROR] [MY-013578] [InnoDB] Server was killed when Innodb Redo logging was disabled. Data files could be corrupt. You can try to restart the database with innodb_force_recovery=6.
  • Redo logging 关闭状态下,不支持 cloning operations 和 redo log archiving 这两个功能;
  • 执行过程中不支持其他并发的 ALTER INSTANCE 操作;

新增内容

  • SQL 语法ALTER INSTANCE {ENABLE | DISABLE} INNODB REDO_LOG
  • INNODB_REDO_LOG_ENABLE 权限,允许执行 Redo Logging 动态开关的操作。
  • Innodb_redo_log_enabled 的 status,用于显示当前 Redo Logging 开关状态。

用法

--先赋权
+GRANT INNODB_REDO_LOG_ENABLE ON *.* to 'data_load_admin';
+--然后关闭redo_log
+ALTER INSTANCE DISABLE INNODB REDO_LOG;
+--确认是否关闭成功
+SHOW GLOBAL STATUS LIKE 'Innodb_redo_log_enabled';
+
+--开始往新实例导入数据
+
+--重新开启redo_log
+ALTER INSTANCE ENABLE INNODB REDO_LOG;
+--确认是否开启成功
+SHOW GLOBAL STATUS LIKE 'Innodb_redo_log_enabled';
+

redolog 归档

热备的原理都是要备份 redolog,由于 redolog 是循环写的。

如果备份期间还是有大量的事务写入,备份速度跟不上 redo log 生成的速度,结果导致 redo log 被覆盖了,然后备份就无法保证一致性。导致备份失败。

MySQL8.0.17 中引入了 redo log 的归档功能,如果我们开启归档功能,redo log 会持续不断的生成,而不会覆盖掉之前的 redo log。

试想这样一种情况,在对一个高并发的数据库进行热备份的时候,备份速度很慢而 redo log 生成的速度很快,备份的速度跟不上 redo log 的生成速度,导致 redo log 被覆盖了,此时备份的一致性就无法得到保证了。

有了 redo log 的归档功能,就可以在备份启动的时候同步启动 redo log 归档,而在备份结束的时候同步停止 redo log 归档,这样就可以避免这个备份的问题了。

备份结束之后,依旧可以利用这个期间产生的 redo log 进行数据恢复。

想要启用 redo log 归档功能,只需设置 innodb_redo_log_archive_dirs 选项即可,该选项可支持在线动态修改,例如:

https://blog.csdn.net/qq_35246620/article/details/79345359

二进制日志 binlog

binlog 是 MySQL 的 server 层很重要的一个文件,它的主要作用如下:

  • 用于复制,在主从复制中,从库利用主库上的 binlog 进行重播,实现主从同步。
  • 用于数据库的基于时间点的还原。

binlog 是逻辑上的日志,

binlog 相关配置

# 相关参数
+
+# binlog_cache是session级别的,也就是说实际binlog cache占用内存数= connections * binlog_cache。
+binlog_cache_size  # 默认值是32k,写binlog之前,会先写binlog_cache
+
+# 一般设置为row
+binlog_format
+
+# binlog是否加密,默认不加密
+binlog_encryption=off
+
+# binlog过期时间,默认是30天
+binlog_expire_logs_seconds=2592000
+

redo-log 和 binlog 是 两阶段提交的重点,

  • 当未开启 binlog 时,InnoDB 通过 redo 和 undo 日志来恢复数据库 (safe crash recovery):

当**数据恢复(指事务已经提交成功,但是数据还没有刷回磁盘时重启的这种情况)**时:

  • 所有已经在存储引擎内部提交的事务应用 redo log 恢复。
  • 所有已经 prepared 但是没有 commit 的事务则会通过 undo log 做回滚

  • 开启 binlog 时,两阶段的流程:

  • InnoDB 的事务 Prepare 阶段,即 SQL 已经成功执行并生成 redo 和 undo 的内存日志;(写 redo_log_buffer)

  • binlog 提交,通过 write() 将 binlog 内存日志数据写入文件系统缓存;
  • sync() 将 binlog 文件系统缓存日志数据永久写入磁盘;(这一步就可以给客户端返回事务提交成功)
  • InnoDB 内部提交,commit 阶段在存储引擎内提交,通过 innodb_flush_log_at_trx_commit 参数控制,使 undo 和 redo 永久写入磁盘。

Prepare 阶段:写 redo-log , 此时 redo log 处于 prepare 状态。注意这里可能只是写 innodb_log_buffer (这是内存中的重做日志缓冲区)

Commit 阶段:innodb 释放锁(释放锁住的资源),释放回滚段,设置提交状态,binlog 持久化到磁盘,然后存储引擎层提交

当关闭 binlog 时,业务你根本不需要 binlog 带给你的特性(比如数据备份恢复、搭建 MySQL 主从集群),那你根本就用不着让 MySQL 写 binlog,也用不着什么两阶段提交。

只用一个 redolog 就够了。无论你的数据库如何 crash,redolog 中记录的内容总能让你 MySQL 内存中的数据恢复成 crash 之前的状态。

所以说,两阶段提交的主要用意是:为了保证 redolog 和 binlog 数据的安全一致性。只有在这两个日志文件逻辑上高度一致了。

你才能放心地使用 redolog 帮你将数据库中的状态恢复成 crash 之前的状态,使用 binlog 实现数据备份、恢复、以及主从复制。

而两阶段提交的机制可以保证这两个日志文件的逻辑是高度一致的。没有错误、没有冲突。

回滚日志 undolog

undolog 是 mysql 中比较重要的事务日志之一,顾名思义,undolog 是一种用于撤销回退的日志,在事务没提交之前,MySQL 会先记录更新前的数据到 undolog 日志文件里面,当事务回滚时或者数据库崩溃时,可以利用 undolog 来进行回退。

为了方便事务回滚,数据库在修改数据时也必须记录数据的旧值,这就是 Undo Log。正因为有了 Redo Log,对于已经提交的事务的数据一定存在于磁盘,所以保证了数据库系统的持久性;而有了 Undo Log,数据库可以保证在故障恢复时对没有完成的事务进行回滚,也就保证的事务的原子性。

默认情况下初始化后,会创建两个 undo 表空间innodb_undo_001innodb_undo_002。对应到磁盘就是innodb_undo_directory下的两个文件undo_001undo_002,如果没有设置该参数,默认就是在datadir目录下。

MySQL 实例最多支持 127 个 undo 表空间,其中包括 MySQL 实例初始化时创建的两个默认 undo 表空间。undo 表空间的数量由 innodb_undo_tablespaces 控制。默认值 0,最大值 95

ALTER UNDO TABLESPACE tablespace_name SET INACTIVE;
+
+DROP UNDO TABLESPACE tablespace_name;
+
+SELECT NAME, STATE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES
+  WHERE NAME LIKE 'tablespace_name';
+
+
+SELECT TABLESPACE_NAME, FILE_NAME FROM INFORMATION_SCHEMA.FILES WHERE FILE_TYPE LIKE 'UNDO LOG';
+
+SHOW STATUS LIKE 'Innodb_undo_tablespaces%';
+

undo 表空间文件的初始大小取决于innodb_page_size值。对于默认的 16k InnoDB 页面大小,初始 undo 表空间文件大小为 10MiB。对于 4k,8k,32k 和 64k 页面大小,初始 undo 表空间文件大小分别为 7MiB,8MiB,20MiB 和 40MiB。

  • 事务回滚

在设计数据库时,我们假设数据库可能在任何时刻,由于如硬件故障,软件 Bug,运维操作等原因突然崩溃。这个时候尚未完成提交的事务可能已经有部分数据写入了磁盘,如果不加处理,会违反数据库对 Atomic 的保证,也就是任何事务的修改要么全部提交,要么全部取消。针对这个问题,直观的想法是等到事务真正提交时,才能允许这个事务的任何修改落盘,也就是 No-Steal 策略。显而易见,这种做法一方面造成很大的内存空间压力,另一方面提交时的大量随机 IO 会极大的影响性能。因此,数据库实现中通常会在正常事务进行中,就不断的连续写入 Undo Log,来记录本次修改之前的历史值。当 Crash 真正发生时,可以在 Recovery 过程中通过回放 Undo Log 将未提交事务的修改抹掉。InnoDB 采用的就是这种方式。

-- 如果业务上有跑批量或者大表的DML操作时,引起大事物,或针对多张大表关联更新时间较长,可能短时间内会将undo"撑大",oracle 我们可以通过创建一个新的 undo,通过在线的替换的方式,将膨胀的 undo 使用 drop 删除以释放空间。
+-- MySQL8.0同样可以使用这种方式来处理,因大事物或长事物引起的undo过大占用空间较多的情况。
+
+-- 添加新的undo文件undo003。mysql8.0中默认innodb_undo_tablespace为2个,不足2个时,不允许设置为inactive,且默认创建的undo受保护,不允许删除。
+-- 将膨胀的 undo 临时设置为inactive,以及 innodb_undo_log_truncate=on,自动 truncate 释放膨胀的undo空间。
+-- 重新将释放空间之后的undo设置为active,可重新上线使用。
+
+-- 在undo遇到大事务并持续增长的情况下,通过新增临时undo,手动释放系统默认的2个undo表空间大小。当然,截断 UNDO 表空间文件对数据库性能有一定影响,尽量在相对空闲时间进行。
+-- 当UNDO表空间被截断时,UNDO表空间中的回滚段将被停用。其他UNDO表空间中的活动回滚段负责整个系统负载,这可能会导致性能略有下降。性能受影响的程度取决于诸多因素
+
+-- 因此,避免潜在性能影响的最简单方法:
+-- 1、就是通过create undo tablespace undo_XXX add datafile ‘/path/undo_xxx.ibu’;多添加几个UNDO表空间;
+-- 2、如果条件允许,磁盘上采用高性能的SSD来存储数据,存储REDO、UNDO等。
+-- 引起UNDO过度膨胀的原因大多是因为基础数据量大,业务并发高,表关联操作较频繁,出现大且长的事物操作,导致UNDO一直处于active状态,不能及时释放回滚段等。
+-- 大事务引起的问题由来已久,即使我们能规避99%的大事物,但实际业务遇到那1%的大事物刚性需求发过来时,还要MySQL各种场景、各种架构和业务层好好磨合。
+

undo 两大作用:

  • 实现事务回滚,保障事务的原子性。事务处理过程中,如果出现了错误或者用户执 行了 ROLLBACK 语句,MySQL 可以利用 undo log 中的历史数据将数据恢复到事务开始之前的状态。

  • 实现 MVCC(多版本并发控制)关键因素之一。MVCC 是通过 ReadView + undo log 实现的。undo log 为每条记录保存多份历史数据,MySQL 在执行快照读(普通 select 语句)的时候,会根据事务的 Read View 里的信息,顺着 undo log 的版本链找到满足其可见性的记录。

组提交

背景

  • 操作系统使用 缓存 来填补内存和磁盘访问的差距,对磁盘文件的写入会先写入到页面缓存中。

  • 在一些 GNU/Linux 和 UNIX 中,使用 **Unix fsync() 系统调用**来把数据刷到磁盘(InnoDB 默认使用 *fsyn*c 这个)

  • 引自 MySQL refman 8.5.8

  • 数据库在事务提交过程中调用 fsync 将数据持久化到磁盘,才满足**ACID**中的**D(持久化)**

  • fsync 是昂贵的操作,对于普通磁盘,每秒能完成几百次 fsync

  • MySQL 中使用了两阶段提交协议,为了满足 D(持久化) ,一次事务提交最多会导致 3 次 fsync

  • 提交的事务在存储引擎内部(redo log)中准备好,一次 fsync;(写 redo-log 到磁盘:/datadit/ib_logfile0)

  • 事务写入到 binlog 中并刷盘持久化,一次 fsync;(写 binlog 到磁盘)

  • 事务在存储引擎内部提交,一次 fsync(写数据文件到磁盘,可以省略,存储引擎准备好的事务可以通过 binlog 来恢复)

虽然上面说的这些 redo log 的刷盘可以通过 innodb_flush_log_at_trx_commit ,binlog 刷盘通过 sync_binlog 参数来控制。

但是 binlog 和 redo log 的刷盘还是会成为最大的开销。通过组提交,将多个事务的 binlog,最大化每次刷盘的收益,弱化磁盘瓶颈,提高性能。

组提交原理

组提交(group commit):

如果多个事务,能在同一时间内并发提交成功,那么就说明这几个事务是不冲突的,逻辑上可以认为是一组事务,在从库上可以并发 replay。

基于 Commit_Order 的并行复制是在主数据库实例事务提交时,写入一些额外信息,从而在从机回放时,可以根据这些信息判断是否可以进行并行的回放。

同一组提交的事务之间是不冲突的,因此可以并行回放。

组提交,就好像我们平时渡船时,一般要等到人坐满后,一次性开船。

组提交将事务分为三个阶段(Flush 阶段、Sync 阶段、Commit 阶段)

每个阶段都会维护一个队列。

  • Flush 阶段:

  • 将 binlog 数据写入文件,当然此时只是写入文件系统的内存缓冲,并不能保证数据库崩溃时 binlog 不丢失。

  • Flush 阶段队列的作用是提供了 Redo log 的组提交

  • 如果在这一步完成后数据库崩溃,由于协调者 binlog 中不保证有该组事务的记录,所以 MySQL 可能会在重启后回滚该组事务

  • Sync 阶段:

参考

https://www.cnblogs.com/JiangLe/p/9650728.html

InnoDB 磁盘结构

创建 InnoDB 表,使用如下语句:

CREATE TABLE t1 (a INT, b CHAR (20), PRIMARY KEY (a)) ENGINE=InnoDB;
+

InnoDB 一般都是默认存储引擎,也可以不用指定 ENGINE。使用如下语句查询 InnoDB 是否默认存储引擎。

SELECT @@default_storage_engine;
+

一个 InnoDB 表和索引,可以在 system tablespace, file-per-table tablespace, ogeneral tablespace 中创建。

默认地,InnoDB 的表都是**独立表空间**。

当 innodb_file_per_table 是 enabled 状态,它是默认的,一个 InnoDB 表被显式创建在单独的表空间中。

如果是 disabled 状态,会创建在系统表空间,如果要用通用表空间,那么使用 CREATE TABLE ... TABLESPACE 语法来创建表。

主键

强烈建议为每个 innodb 表设立主键。

创建外部表

有时候,可能需要创建外部表(即在 datadir 外部创建表),可能是由于空间管理,IO 优化等原因。

InnoDB 支持外部表的语法:

-- 第一种情况,使用 DATA DIRECTORY 子句
+CREATE TABLE t1 (c1 INT PRIMARY KEY) DATA DIRECTORY = '/external/directory';
+

导入 InnoDB 表

表空间传输特性

  • 复制数据到新实例。
  • 从备份表空间中恢复数据
  • 比 dump 表更快的方式(dump 需要重新插入数据和重建索引)

前提:

  • innodb_file_per_table 变量必须开启,默认就是开启的。
  • innodb_page_size 必须相等。
  • 如果一个表有外键关系,在执行 DISCARD TABLESPACE 语句之前必须先关掉

https://blog.k4nz.com/7bbf69045e0da119a1a892e054c6d145/

InnoDB 引擎之-异步 IO(Async IO)和预读

在 MySQL 中,InnoDB 的异步 IO 主要是用来处理预读以及对数据文件的写请求的。而对于正常的页面数据读取则是通过同步 IO 进行的。

这个是由 innodb_use_native_aio 参数控制的。它适用于 Linux 操作系统,并且默认启用。操作系统需要 libaio 库。

InnoDB 在 I/O 的优化上有个比较重要的特性为预读,预读请求是一个 i/o 请求,它会异步地将多个页面预读入缓冲池,预计很快就会需要这些页面,这些请求在一个范围内引入所有页面。

程序是有空间局部性的,靠近当前被访问数据的数据,在未来很大概率会被访问到。

所以,MySQL 在加载数据页时,会提前把它相邻的数据页一并加载进来,目的是为了减少磁盘 IO。

但是可能这些被提前加载进来的数据页,并没有被访问,相当于这个预读是白做了,这个就是预读失效。

如果使用简单的 LRU 算法,就会把预读页放到 LRU 链表头部,而当 Buffer Pool 空间不够的时候,还需要把末尾的页淘汰掉。

如果这些预读页如果一直不会被访问到,就会出现一个很奇怪的问题,不会被访问的预读页却占用了 LRU 链表前排的位置,而末尾淘汰的页,可能是频繁访问的页,这样就大大降低了缓存命中率。

数据库请求数据的时候,会将读请求交给文件系统,放入请求队列中;相关进程从请求队列中将读请求取出,根据需求到相关数据区(内存、磁盘)读取数据;取出的数据,放入响应队列中,最后数据库就会从响应队列中将数据取走,完成一次数据读操作过程。

接着进程继续处理请求队列,(如果数据库是全表扫描的话,数据读请求将会占满请求队列),判断后面几个数据读请求的数据是否相邻,再根据自身系统 IO 带宽处理量,进行预读,进行读请求的合并处理,一次性读取多块数据放入响应队列中,再被数据库取走。(如此,一次物理读操作,实现多页数据读取,rrqm>0(# iostat -x),假设是 4 个读请求合并,则 rrqm 参数显示的就是 4)

InnoDB 使用两种预读算法来提高 I/O 性能:线性预读(linear read-ahead)和随机预读(randomread-ahead)

为了区分这两种预读的方式,我们可以把线性预读放到以 extent 为单位,而随机预读放到以 extent 中的 page 为单位。线性预读着眼于将下一个 extent 提前读取到 buffer pool 中,而随机预读着眼于将当前 extent 中的剩余的 page 提前读取到 buffer pool 中。

线性预读(linear read-ahead):它可以根据顺序访问缓冲池中的页面,预测哪些页面可能需要很快。通过使用配置参数 innodb_read_ahead_threshold,通过调整触发异步读取请求所需的顺序页访问数,可以控制 Innodb 执行提前读操作的时间。在添加此参数之前,InnoDB 只会计算当在当前范围的最后一页中读取整个下一个区段时是否发出异步预取请求。

线性预读方式有一个很重要的变量控制是否将下一个 extent 预读到 buffer pool 中,通过使用配置参数 innodb_read_ahead_threshold,可以控制 Innodb 执行预读操作的时间。如果一个 extent 中的被顺序读取的 page 超过或者等于该参数变量时,Innodb 将会异步的将下一个 extent 读取到 buffer pool 中,innodb_read_ahead_threshold 可以设置为 0-64 的任何值,默认值为 56,值越高,访问模式检查越严格。

表空间

InnoDB 表空间(Tablespace)可以看做一个逻辑概念,InnoDB 把数据保存在表空间,本质上是一个或多个磁盘文件组成的虚拟文件系统。

系统表空间

系统表空间是 change buffer 的存放区域。如果没有启用独立表空间,也会在其中存放业务表数据和索引数据。

在老版本 MySQL,InnoDB 数据字典也存放在系统表空间中。doublewrite buufer 也存放在系统表空间中,在 8.0.20 之后,doublewrite buufer 也存在单独的文件中了。

系统表空间可以由多个文件组成,默认是一个,名为 ibdata1,默认放在 datadir 目录下面。

由如下参数控制

# 文件路径,默认是 datadir 下,也可以自定义路径
+innodb_data_home_dir=/myibdata/
+
+# 参数语法:文件名:文件初始大小(初始大小不低于12M):自增长属性:最大属性
+innodb_data_file_path=file_name:file_size[:autoextend[:max:max_file_size]]
+
+# 每次自动扩展的增量大小,由innodb_autoextend_increment控制,单位为M,默认是64M
+innodb_autoextend_increment=64
+
+# 默认值如下:ibdata1:12M:自增长
+innodb_data_file_path=ibdata1:12M:autoextend
+
+# 也可以一次定义两个系统表空间文件
+innodb_data_file_path=ibdata1:50M;ibdata2:50M:autoextend
+

也可以手动调整增长 ibdata 文件

# 先关停数据库
+
+# 如果ibdata设置了自增长属性,删掉它
+

单独表空间

MySQL 5.6.6 之前的版本,InnoDB 默认会将所有的数据库 InnoDB 引擎的表数据存储在一个共享空间中:ibdata1,这样就会让管理感觉很难受,增删数据库的时候,ibdata1 文件不会自动收缩。

单个数据库的备份也将成为问题。通常只能将数据使用 mysqldump 导出,然后再导入解决这个问题。

在之后的版本,为了优化上述问题,独立表空间 innodb_file_per_table 参数默认开启

mysql> show variables like 'innodb_file_per_table';
++-----------------------+-------+
+| Variable_name         | Value |
++-----------------------+-------+
+| innodb_file_per_table | ON    |
++-----------------------+-------+
+1 row in set, 1 warning (0.05 sec)
+

独立表空间就是每个表单独创建一个 .ibd 文件,该文件存储着该表的索引和数据。由 innodb_file_per_table 变量控制。禁用 innodb_file_per_table 会导致 InnoDB 在系统表空间中创建表。

InnoDB 表空间文件 .ibd 初始大小为 96K,而 InnoDB 默认页大小为 16K,页大小也可以通过 innodb_page_size 配置。

在 ibd 文件中,0-16KB 偏移量即为 0 号数据页,16KB-32KB 的为 1 号数据页,以此类推。

页的头尾除了一些元信息外,还有 Checksum 校验值,这些校验值在写入磁盘前计算得到,当从磁盘中读取时,重新计算校验值并与数据页中存储的对比,如果发现不同,则会导致 MySQL 崩溃。

为什么会出现

InnoDB 是基于**磁盘**存储的,并将其中的数据按**页的方式**进行管理。因此 InnoDB 可视为基于磁盘的数据库系统。由于 CPU 的速度和磁盘 IO 速度的巨大鸿沟,需要缓冲池来提高数据库的整体性能

是什么

缓冲池是主内存中的一个区域,在InnoDB访问表和索引数据时会在其中进行 高速缓存。缓冲池允许直接从内存中处理经常使用的数据,从而加快了处理速度。在专用服务器上,通常将多达 80%的物理内存分配给缓冲池。

为了提高大容量读取操作的效率,缓冲池被分为多个页面,这些页面可能包含多个行。为了提高缓存管理的效率,缓冲池被实现为页面的链接列表。使用LRU算法的变体将很少使用的数据从缓存中老化掉 。

知道如何利用缓冲池将经常访问的数据保留在内存中是 MySQL 优化的重要方面。

架构

简要架构图

1.2.0.InnoDB缓冲池的简要结构.png

关键概念 —— 数据页

它是 MySQL 抽象出来的数据单位,磁盘文件中就是存放了很多数据页,每个数据页里存放了很多行数据。

默认情况下,数据页的大小是 16kb。大概结果如下图所示

1.5.8.InnoDB中B+树的层次结构

所以对应的,在 Buffer Pool 中,也是以数据页为数据单位,存放着很多数据。但是我们通常叫做缓存页,因为 Buffer Pool 毕竟是一个缓冲池,并且里面的数据都是从磁盘文件中缓存到内存中。

所以,默认情况下缓存页的大小也是 16kb,因为它和磁盘文件中数据页是一一对应的。

所以,缓冲池和磁盘之间的数据交换的单位是数据页,包括从磁盘中读取数据到缓冲池和缓冲池中数据刷回磁盘中,如图所示:

1.5.23.缓冲池刷新到磁盘简要图

怎么做的

  • 第一次读取数据时,首先从磁盘中读取数据页,并放到(FIX)缓冲池中。
  • 再次读取同样的数据时,先看缓冲池中是否有相同的数据页。有则命中,从缓冲池中读取。否则从磁盘读取

缓冲池是 InnoDB 存储引擎中最重要的组件。因为为了提高 MySQL 的并发性能,使用到的数据都会缓存在缓冲池中,然后所有的增删改查操作都将在缓冲池中执行。

通过这种方式,保证每个更新请求,尽量就是**只更新内存,然后往磁盘顺序写日志文件**。

更新内存的性能是极高的,然后顺序写磁盘上的日志文件的性能也是比较高的,因为顺序写磁盘文件,他的性能要远高于随机读写磁盘文件。

服务器的内存大小很重要

mysql> show variables like 'innodb_buffer_pool_size';
++-------------------------+---------+
+| Variable_name           | Value   |
++-------------------------+---------+
+| innodb_buffer_pool_size | 8388608 |
++-------------------------+---------+
+1 row in set (0.02 sec)
+

缓冲池中缓存的数据页类型

  • 索引页
  • 数据页
  • undo 页
  • 插入缓冲(Insert buffer)
  • 自适应哈希索引
  • 锁信息
  • 数据字典信息(data dictionary)

怎么识别数据在哪个缓存页中?

到此,我们都知道 Buffer Pool 中是用缓存页来缓存数据的,但是我们怎么知道缓存页对应着哪个表,对应着哪个数据页呢?

所以每个缓存页都会对应着一个描述数据块,里面包含数据页所属的表空间、数据页的编号,缓存页在 Buffer Pool 中的地址等等。

描述数据块本身也是一块数据,它的大小大概是缓存页大小的 5%左右,大概 800 个字节左右的大小。

描述如图所示:

1.5.24.缓冲池中的描述数据块

缓冲池实例

概念

即缓冲池的个数。每页根据哈希值分配到不同缓冲池实例

好处

减少资源竞争、支持更大的并发处理,加快查询速度

查看缓冲池实例个数

查看缓冲池实例个数(默认为 1)

mysql> show variables like 'innodb_buffer_pool_instances';
++------------------------------+-------+
+| Variable_name                | Value |
++------------------------------+-------+
+| innodb_buffer_pool_instances | 1     |
++------------------------------+-------+
+1 row in set (0.03 sec)
+

​ 可通过配置文件修改实例个数

Buffer Pool 如何应对高并发场景

1、单个 Buffer Pool 的问题

直到现在,估计大家都以为缓冲池只是一个大的内存区域,在 InnoDB 存储引擎中只有一个,这是对的吗?

我们可以想象,如果 InnoDB 存储引擎只有一个 Buffer Pool,当高并发时,多个请求进来,那么为了保证数据的一致性(缓存页、free 链表、flush 链表、lru 链表等多种操作),必须得给缓冲池加锁了,每一时刻只能有一个请求获得锁去操作 Buffer Pool,其他请求只能排队等待锁释放。那么此时 MySQL 的性能是多么的低!

2、多个 Buffer Pool

既然一个 Buffer Pool 不够用,那么整多几个呗。

在生产环境中,其实我们是可以给 MySQL 设置多个 Buffer Pool 来提升 MySQL 的并发能力的~

如何设置?

我们先看看 MySQL 的默认规则:如果你给 Buffer Pool 分配的内存小于 1GB,那么最多就只会给你一个 Buffer Pool

但是呢,如果你给 MySQL 设置的内存很大,此时你可以利用下面两个参数来设置 Buffer Pool 的总大小和总实例数,这样,MySQL 就能有多个 Buffer Pool 来支撑高并发了。

[server]
+innodb_buffer_pool_size = 8589934592
+innodb_buffer_pool_instances = 4
+

解释一下:上面利用参数 innodb_buffer_pool_size 来设置 Buffer Pool 的总大小为 8G,利用参数 innodb_buffer_pool_instances 来设置一共有 4 个 Buffer Pool 实例。那么就是说,MySQL 一共有 4 个 Buffer Pool ,每个的大小为 2G。

当然了,每个 Buffer Pool 负责管理着自己的描述数据块和缓存页,有自己独立一套 free 链表、flush 链表和 lru 链表。

到这,我们就晓得,只要你能分配足够大的内存给 Buffer Pool ,你就能创建尽量多的 Buffer Pool 来应对高并发场景~

正所谓,并发性能不高,机器配置来凑,这还是有道理的。

但是正经点来说,最基本最主要的还是咱们写的 SQL。当然了,能写出一手好 SQL,前提我们还是得理解 MySQL 各个组件的原理,熟悉索引的原理、事务原理和锁原理等。当然了,之后我也会分别对这些做出一个学习总结分享出来。

3、生产环境中,如何动态调整 Buffer Pool 的大小

相信基本每个公司,项目上线后,用户和流量会不断地增长,这对于 MySQL 来说,会有什么变化?

首先,访问增多,不断地从磁盘文件中的数据页读取数据到 Buffer Pool,也不断地将 Buffer Pool 的脏缓存页刷回磁盘文件中。很明显的,Buffer Pool 越小,这两个操作就会越频繁,但是磁盘 IO 操作又是比较耗时的,本来 SQL 执行只要 20 ms,如果碰巧碰到遇到缓存页用完,就要经历一系列的操作,SQL 最后执行完可能就要 200 ms,甚至更多了。

所以我们此时需要及时调整 Buffer Pool 的大小。

Ⅰ、如何动态调整 Buffer Pool 的大小?

但是生产环境,肯定不能让我们直接修改 MySQL 配置然后再重启吧,这估计要骂死。

在 MySQL 5.7 后,MySQL 允许我们动态调整参数 innodb_buffer_pool_size 的值来调整 Buffer Pool 的大小了。

假如就这样直接调大会存在啥问题?

假设调整前的配置:Buffer Pool 的总大小为 8G,一共 4 个 Buffer Pool,每个大小为 2G。

[server]
+innodb_buffer_pool_size = 8589934592
+innodb_buffer_pool_instances = 4
+

假设给 Buffer Pool 调整到 16 G,就是说参数 innodb_buffer_pool_size 改为 17179869184。

此时,MySQL 会为 Buffer Pool 申请一块大小为 16G 的连续内存,然后分成 4 块,接着将每一个 Buffer Pool 的数据都复制到对应的内存块里,最后再清空之前的内存区域。

我们可以发现,全部数据要从一块地方复制到另外一块地方,那这是相当耗费时间的操作,整整 8 个 G 的数据要进行复制粘贴呢!而且,如果本来 Buffer Pool 是更大的话,那就更恐怖了。

Ⅱ、Buffer Pool 的 chunk 机制

为了解决上面的问题,Buffer Pool 引入一个机制:chunk 机制。

  1. 每个 Buffer Pool 其实是由多个 chunk 组成的。每个 chunk 的大小由参数 innodb_buffer_pool_chunk_size 控制,默认值是 128M。
  2. 每个 chunk 就是一系列的描述数据块和对应的缓存页。
  3. 每个 Buffer Pool 里的所有 chunk 共享一套 free、flush、lru 链表。

得益于 chunk 机制,就能避免了上面说到的问题。当扩大 Buffer Pool 内存时,不再需要全部数据进行复制和粘贴,而是在原本的基础上进行增减内存。

下面继续用上面的例子,介绍一下 chunk 机制下,Buffer Pool 是如何动态调整大小的。

调整前 Buffer Pool 的总大小为 8G,调整后的 Buffer Pool 大小为 16 G。

由于 Buffer Pool 的实例数是不可以变的,所以是每个 Buffer Pool 增加 2G 的大小,此时只要给每个 Buffer Pool 申请 (2000M/128M)个 chunk 就行了,但是要注意的是,新增的每个 chunk 都是连续的 128M 内存。

4、生产环境中,应该给 Buffer Pool 设置多大的内存

Ⅰ、如何设置 Buffer Pool 的总内存大小

我们都知道,给 Buffer Pool 分配越大的内存,MySQL 的并发性能就越好。那是不是都应该将百分之九十九的机器的内存都分配给 Buffe Pool 呢?

那当然不是了!

先不说操作系统内核也需要几个 G 内存,MySQL 除了 Buffer Pool 还有很多别的内存数据结构呢,这些都是需要内存的,所以说,上面的想法是绝对不行的!

比较合理的比例,应该是 Buffer Pool 的内存大小占机器总内存的 50% ~ 60%,例如机器的总内存有 32G,那么你给 Buffer Pool 分配个 20G 左右就挺合理的了。

Ⅱ、如何设置 Buffer Pool 的实例数

Buffer Pool 的总大小搞定了,那应该设置多少个实例数呢?

这里有一个公式:Buffer Pool 总大小 = (chunk 大小 * Buffer Pool 数量)* n 倍

上个例子解释一下。

假设此时 Buffer Pool 的总大小为 8G,即 8192M,那么 Buffer Pool 的数量应该是多少个呢?

8192 = ( 128 _ Buffer Pool 数量)_ n

64 个:也是可以的,但是每个 Buffer Pool 就只要一个 chunk。

16 个:也是可以的,每个 Buffer Pool 拥有四个 chunk。

8 个:也是可以的,每个 Buffer Pool 拥有八个 chunk。

所以说,只要你的 Buffer Pool 数量符合上面的公式,其实都是可以的,看你们根据业务后怎么选择了。

Buffer Pool 的初始化

到此,我们都知道了,Buffer Pool 是缓存数据的数据单位为缓存页,利用描述数据块来标识缓存页。

那么,MySQL 启动时,是如何初始化 Buffer Pool 的呢?

  1. MySQL 启动时,会根据参数 innodb_buffer_pool_size 的值来为 Buffer Pool 分配内存区域。

  2. 然后会按照缓存页的默认大小 16k 以及对应的描述数据块的 800 个字节 左右大小,在 Buffer Pool 中划分中一个个的缓存页和一个个的描述数据库块。

  3. 注意,此时的缓存页和描述数据块都是空的,毕竟才刚启动 MySQL 呢。

doublewrite 特性

介绍 double write 之前我们有必要了解partial page write(部分页失效)问题。

InnoDB 的 Page Size 一般是 16KB,其数据校验也是针对这 16KB 来计算的。将数据写入到磁盘是以 Page 为单位进行操作的。我们知道,由于文件系统对一次大数据页(例如 InnoDB 的 16KB)大多数情况下不是原子操作,这意味着如果服务器宕机了,可能只做了部分写入。假如 16K 的数据,写入 4K 时,发生了系统断电/os crash ,只有一部分写是成功的,这种情况下就是 partial page write 问题。

有经验的 DBA 可能会想到,如果发生写失效,MySQL 可以根据 redo log 进行恢复。这是一个办法,但是必须清楚地认识到,redo log 中记录的是对页的物理修改(redo 的最小原子单位是数据库中的页这个概念)。如偏移量 800,写"aaaa"记录。如果这个页本身已经发生了损坏,再对其进行重做是没有意义的。MySQL 在恢复的过程中检查 page 的 checksum,checksum 就是检查 page 的最后事务号,发生 partial page write 问题时,page 已经损坏,找不到该 page 中的事务号。在 InnoDB 看来,这样的数据页是无法通过 checksum 验证的,就无法恢复。即时我们强制让其通过验证,也无法从崩溃中恢复,因为当前 InnoDB 存在的一些日志类型,有些是逻辑操作,并不能做到幂等。

为了解决这个问题,InnoDB 实现了 double write buffer,简单来说,就是在写数据页之前,先把这个数据页写到一块独立的物理文件位置(ibdata 文件,在 8.0.20 之后,doublewrite buufer 也存在单独的文件中了)。然后再写到数据页。这样在宕机重启时,如果出现数据页损坏,那么在应用 redo log 之前,需要通过该页的副本来还原该页,然后再进行 redo log 重做,这就是 double write。double write 技术带给 innodb 存储引擎的是数据页的可靠性。

参考

LRU list

传统的 LRU

按照英文的直接原义就是 Least Recently Used,最近最久未使用。

由来

它是按照一个非常著名的计算机操作系统基础理论得来的:最近使用的页面数据会在未来一段时期内仍然被使用,已经很久没有使用的页面很有可能在未来较长的一段时间内仍然不会被使用。基于这个思想,会存在一种缓存淘汰机制,每次从内存中找到**最久未使用的数据然后置换出来**,从而存入新的数据!

作用

它的主要衡量指标是**使用的时间**,

(附加指标是**使用的次数**)

在计算机中大量使用了这个机制,它的合理性在于**优先筛选热点数据**,所谓热点数据,就是**最近最多使用的数据**!

特征

  • 最新的数据会插入对列前面
  • 访问列表中旧的数据,会使它重新置于列表顶部

InnoDB 中的 LRU

缓冲池中,页的大小也是 16KB

与传统 LRU 特殊的地方

新增了 midPoint 位置。新读取到的页并没有直接放在 LRU 列的首部,而是放在距离尾部 37%的位置。这个算法称之为**midpoint insertion stategy**。

  • 即 midPoint 在整体列表的 ⅝ 处

  • midpoint 之前的是 new 区域(热数据)

  • midpoint 之后的数据是不活跃数据,old 区域。

  • midpoint 处,是新子列表的尾部与旧子列表的头相交的边界
  • 当 InnoDB 将页面读入缓冲池时,它首先将其插入中点(旧子列表的头部)。因为它是用户发起的操作(如 SQL 查询)所需的,或者是 InnoDB 自动执行的预读操作的一部分,所以这些页面可以被读取。
  • 访问旧子列表中的页面会使其变得“年轻”,并将其移动到新子列表的头部。如果由于用户启动的操作需要该页而读取该页,则会立即进行第一次访问,并使该页变得年轻。如果该页是由于预读操作而被读取的,则第一次访问不会立即发生,并且在该页被逐出之前可能根本不会发生。
  • 当数据库运行时,缓冲池中未被访问的页通过向列表的尾部移动来“老化”。新子列表和旧子列表中的页面都会随着其他页面变为新页面而老化。旧子列表中的页面也会随着页面插入到中点处而老化。最终,未使用的页面到达旧子列表的尾部并被逐出。

1.2.1.InnoDB中的LRU

查看 midpoint

mysql> show variables like 'innodb_old_blocks_pct';
++-----------------------+-------+
+| Variable_name         | Value |
++-----------------------+-------+
+| innodb_old_blocks_pct | 37    |
++-----------------------+-------+
+1 row in set (0.04 sec)
+
  • 37:末尾处的 37%的位置,即末尾 ⅜ 的位置

为什么不把最新查到的数据放到首部

例如**索引扫描**或**数据扫描** / 全表扫描,会使大量的缓冲池中大量的页被刷新出去。然而被扫描到的数据页只是本次操作所需要的,并非热点数据。而真正的热点数据还是从磁盘读取,影响了缓冲池效率。

MySQL 为了提高性能,提供了一个机制:预读机制

当你从磁盘上加载一个数据页的时候,他可能会连带着把这个数据页相邻的其他数据页,也加载到缓存里去。这个机制会带来这么一个问题:连带的数据页可能在后面的查询或者修改中,并不会用到,但是它们却在 lru 链表的头部。

若还未理解,可参阅官方文档:防止缓冲池扫描

缓冲池可调整的配置

  • 理想情况下,您可以将缓冲池的大小设置为尽可能大的值,从而为服务器上的其他进程留出足够的内存来运行,而不需要过多的分页。缓冲池越大,InnoDB 就越像内存中的数据库,从磁盘读取数据一次,然后在后续读取期间访问内存中的数据。但并不是越大越好

  • 在具有足够内存的 64 位系统上,您可以将缓冲池拆分为多个部分,以最大限度地减少并发操作之间对内存结构的争用。即增加缓冲池实例

  • 您可以将频繁访问的数据保留在内存中,而不考虑会将大量不经常访问的数据引入缓冲池的操作的突然活动高峰。可以调整 midpoint 的位置。

  • 您可以控制如何以及何时执行预读请求,以便在预期很快需要页面的情况下将页面异步预取到缓冲池中。

  • 您可以控制何时发生后台刷新,以及是否根据工作负载动态调整刷新速率

  • 您可以配置 InnoDB 如何保留当前的缓冲池状态,以避免服务器重启后过长的预热期

  • 控制最新数据页放到热点区域的时间,达到这个时间后,就可以把数据放到热点区域。

mysql> show variables like 'innodb_old_blocks_time';
++------------------------+-------+
+| Variable_name          | Value |
++------------------------+-------+
+| innodb_old_blocks_time | 1000  |
++------------------------+-------+
+1 row in set (0.04 sec)
+
  • 数据从 old 转到 new,此操作称为 page made young
  • 数据未从 old 转到 new,此操作称为 page not made young

使用 InnoDB 标准监视器监视缓冲池

InnoDB可以使用访问的标准监视器输出, SHOW ENGINE INNODB STATUS提供有关缓冲池操作的度量。缓冲池度量标准位于BUFFER POOL AND MEMORYInnoDB标准监视器”输出中的部分,其输出类似于以下内容:

----------------------
+BUFFER POOL AND MEMORY
+----------------------
+Total large memory allocated 2198863872
+Dictionary memory allocated 776332
+Buffer pool size   131072
+Free buffers       124908
+Database pages     5720
+Old database pages 2071
+Modified db pages  910
+Pending reads 0
+Pending writes: LRU 0, flush list 0, single page 0
+Pages made young 4, not young 0
+0.10 youngs/s, 0.00 non-youngs/s
+Pages read 197, created 5523, written 5060
+0.00 reads/s, 190.89 creates/s, 244.94 writes/s
+Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not
+0 / 1000
+Pages read ahead 0.00/s, evicted without access 0.00/s, Random read
+ahead 0.00/s
+LRU len: 5720, unzip_LRU len: 0
+I/O sum[0]:cur[0], unzip sum[0]:cur[0]
+

下表描述了InnoDB标准监视器报告的缓冲池度量 标准

名称 描述
Total memory allocated 为缓冲池分配的总内存(以字节为单位)
Dictionary memory allocated InnoDB数据字典分配的总内存,以字节为单位
Buffer pool size 分配给缓冲池的页面总大小
Free buffers 缓冲池空闲列表的页面总大小
Database pages 缓冲池 LRU 列表的页面总大小
Old database pages 缓冲池旧 LRU 子列表的页面总大小
Modified db pages 缓冲池中当前修改的页面数
Pending reads 等待读入缓冲池的缓冲池页面数
Pending writes LRU 从 LRU 列表的底部开始写入的缓冲池中的旧脏页数
Pending writes flush list 检查点期间要刷新的缓冲池页面数
Pending writes single page 缓冲池中暂挂的独立页面写入数
Pages made young 缓冲池 LRU 列表中变年轻的页面总数(已移至“ new ”页面的子列表的开头)
Pages made not young 缓冲池 LRU 列表中没有年轻的页面总数(保留在“ old ”子列表中但没有年轻的页面)
youngs/s 每秒平均访问缓冲池 LRU 列表中的旧页面所导致的页面年轻
non-youngs/s 每秒平均访问缓冲池 LRU 列表中的旧页面导致的页面不年轻
Pages read 从缓冲池读取的页面总数
Pages created 在缓冲池中创建的页面总数
Pages written 从缓冲池写入的页面总数
reads/s 每秒平均每秒读取的缓冲池页面数
creates/s 每秒平均创建的缓冲池页面的每秒数量
writes/s 每秒平均缓冲池页面写入数
Buffer pool hit rate 从缓冲池内存与磁盘存储读取的页面的缓冲池页面命中率
young-making rate 页面访问的平均命中率使页面更年轻
not (young-making rate) 页面访问未使页面变年轻的平均命中率
Pages read ahead 预读操作的每秒平均数
Pages evicted without access 每秒从缓冲池访问而未访问的页面的平均值
Random read ahead 随机预读操作的每秒平均数
LRU len 缓冲池 LRU 列表的页面总大小
unzip_LRU len 缓冲池 unzip_LRU 列表的页面总大小
I/O sum 最近 50 秒内访问的缓冲池 LRU 列表页面的总数
I/O cur 已访问的缓冲池 LRU 列表页面的总数
I/O unzip sum 已访问的缓冲池 unzip_LRU 列表页面的总数
I/O unzip cur 已访问的缓冲池 unzip_LRU 列表页面的总数
  • youngs/s 仅适用于 old 页。它基于对页面的**访问次数**,而不是页面数量。可以对给定页面进行多次访问,所有这些访问都会被计算在内。如果在没有进行大型扫描的情况下看到非常低的 Young/s 值,则可能需要减少延迟时间或增加用于旧子列表的缓冲池的百分比。增加百分比会使旧子列表变大,因此该子列表中的页面需要更长时间才能移动到尾部,这会增加再次访问这些页面并使其年轻化的可能性。
  • non-youngs/s 仅适用于旧页。它基于对页面的访问次数,而不是页面数量。可以对给定页面进行多次访问,所有这些访问都会被计算在内。如果在执行大型表格扫描时未看到更高的**non-youngs/s值(以及更高的youngs/s**值),请增加延迟值。
  • young-making rate 该值考虑了对所有缓冲池页面的访问,而不仅仅是对旧子列表中的页面的访问。young-making rate 和 not-young-making rate 通常不等于总缓冲池命中率。旧子列表中的页面命中会导致页面移动到新子列表,而新子列表中的页面命中会导致页面仅在与头部有一定距离时才移动到列表的头部。
  • not (young-making rate) Not(Young-Making Rate)是由于未满足 Innodb_old_block_time 定义的延迟,或由于新子列表中的页面命中未导致页面移动到页头而导致页面访问未导致页面年轻的平均命中率。此速率考虑了对所有缓冲池页面的访问,而不仅仅是对旧子列表中的页面的访问。

缓冲池服务器状态变量INNODB_BUFFER_POOL_STATS表提供了许多与InnoDB标准监视器输出中相同的缓冲池度量 标准

unzip_LRU

InnoDB 从 1.0.X 开始支持页压缩技术。原本 16k 的页,可以压缩到 2k、4k、8k。因此需要 unzip_LRU 列来管理,但是注意:LRU list 中包含了 unzip_LRU

如何给 unzip_LRU 分配内存:(假设需要从缓冲池中申请 4KB 大小的内存)

  • 检查 4KB 的 unzip_LRU 列表,检查是否有可用的空闲页
  • 若有,则直接使用
  • 否则申请 8KB 的 unzip_LRU 页
  • 若能申请到页,则拆分成两个 4KB 页,并存放到 unzip_LRU 列表
  • 如果申请不到,则从 LRU 列表申请一个 16K 的页面,拆分成一个 8K,两个 4k,分别放到对应的 unzip_LRU 列表中

Free list

结构

它是一个双向链表,链表的每个节点就是一个个空闲的缓存页对应的描述数据块。

他本身其实就是由 Buffer Pool 里的描述数据块组成的,你可以认为是每个描述数据块里都有两个指针,一个是 free_pre 指针,一个是 free_next 指针,分别指向自己的上一个 free 链表的节点,以及下一个 free 链表的节点。

通过 Buffer Pool 中的描述数据块的 free_pre 和 free_next 两个指针,就可以把所有的描述数据块串成一个 free 链表。

下面我们可以用伪代码来描述一下 free 链表中描述数据块节点的数据结构:

DescriptionDataBlock{
+    block_id = block1;
+    free_pre = null;
+    free_next = block2;
+}
+

free 链表有一个基础节点,他会引用链表的头节点和尾节点,里面还存储了链表中有多少个描述数据块的节点,也就是有多少个空闲的缓存页。

下面我们也用伪代码来描述一下基础节点的数据结构:

FreeListBaseNode{
+    start = block01;
+    end = block03;
+    count = 2;
+}
+

到此,free 链表就介绍完了。上面我们也介绍了 MySQL 启动时 Buffer Pool 的初始流程,接下来,我会将结合刚介绍完的 free 链表,讲解一下 SQL 进来时,磁盘数据页读取到 Buffer Pool 的缓存页的过程。但是,我们先要了解一下一个新概念:数据页缓存哈希表,它的 key 是表空间+数据页号,而 value 是对应缓存页的地址。

描述如图所示:

1.5.25.数据页缓存哈希表

磁盘数据页读取到 Buffer Pool 的缓存页的过程

  1. 首先,SQL 进来时,判断数据对应的数据页能否在 数据页缓存哈希表里 找到对应的缓存页。

  2. 如果找到,将直接在 Buffer Pool 中进行增删改查。

  3. 如果找不到,则从 free 链表中找到一个空闲的缓存页,然后从磁盘文件中读取对应的数据页的数据到缓存页中,并且将数据页的信息和缓存页的地址写入到对应的描述数据块中,然后修改相关的描述数据块的 free_pre 指针和 free_next 指针,将使用了的描述数据块从 free 链表中移除。记得,还要在数据页缓存哈希表中写入对应的 key-value 对。最后也是在 Buffer Pool 中进行增删改查。

Free list 和 LRU List 关系

数据库刚启动时,LRU list 是空的。Free list 是最大的。当需要从缓冲池中分页时,看 Free list 有空闲页:

  • 有则删除 Free list 的页,加入到 LRU list 中。维持一个数量平衡

  • 否则,根据 LRU 算法,淘汰 LRU 末尾的页,省出内存来,分配给新的页

  • lru 链表尾部的缓存页何时刷入磁盘?

当 free list 为空了,此时需要将数据页加载到缓冲池里,就会 lru list 的 old 数据区域尾部的缓存页刷入磁盘,然后清空,再加载数据页的数据。

一条后台线程,运行一个定时任务,定时将 lru list 的 old 数据区域的尾部的一些缓存页刷入磁盘,然后清空,最后把他们对应的描述数据块加入到 free 链表中去。

当然了,除了 lru list 尾部的缓存页会被刷入磁盘,还有的就是 flush list 的缓存页。

后台线程同时也会在 MySQL 不繁忙的时候,将 flush 链表中的缓存页刷入磁盘中,这些缓存页的描述数据块会从 lru 链表和 flush 链表中移除,并加入到 free 链表中。

Flush list

脏页(dirty page)

我们都知道 SQL 的增删改查都在 Buffer Pool 中执行,慢慢地,Buffer Pool 中的缓存页因为不断被修改而导致和磁盘文件中的数据不一致了,也就是 Buffer Pool 中会有很多个脏页,脏页里面很多脏数据。

所以,MySQL 会有一条后台线程,定时地将 Buffer Pool 中的脏页刷回到磁盘文件中。

但是,后台线程怎么知道哪些缓存页是脏页呢,不可能将全部的缓存页都往磁盘中刷吧,这会导致 MySQL 暂停一段时间。

MySQL 通过 checkPoint 技术将脏页刷新到磁盘

  • Flush list 中的页,即为脏页
  • 脏页既存在于 LRU 列表中,也存在于 Flush list 中(LRU 列表用来管理缓冲池中页的可用性,Flush list 脏页刷新到磁盘,两者互不影响)

结构

Flush list 也是通过缓存页的描述数据块中的两个指针,让修改过的缓存页的描述数据块能串成一个双向链表,这两指针大家可以认为是 flush_pre 指针和 flush_next 指针。

下面我用伪代码来描述一下:

DescriptionDataBlock{
+    block_id = block1;
+    // free 链表的
+    free_pre = null;
+    free_next = null;
+
+    // flush 链表的
+    flush_pre = null;
+    flush_next = block2;
+}
+

flush 链表也有对应的基础节点,也是包含链表的头节点和尾节点,还有就是修改过的缓存页的数量。

FlushListBaseNode{
+    start = block1;
+    end = block2;
+    count = 2;
+}
+

到这里,我们都知道,SQL 的增删改都会使得缓存页变为脏页,此时会修改脏页对应的描述数据块的 flush_pre 指针和 flush_next 指针,使得描述数据块加入到 flush 链表中,之后 MySQL 的后台线程就可以将这个脏页刷回到磁盘中。

描述如图所示:

1.5.26.flush list结构

InnoDB 表压缩

数据库中的表是由一行行记录(rows)所组成,每行记录被存储在一个页中,在 MySQL 中,一个页的大小默认为 16K,一个个页又组成了每张表的表空间。

通常我们认为, 如果一个页中存放的记录数越多,数据库的性能越高 。这是因为数据库表空间中的页是存放在磁盘上,MySQL 数据库先要将磁盘中的页读取到内存缓冲池,然后以页为单位来读取和管理记录。

一个页中存放的记录越多,内存中能存放的记录数也就越多,那么存取效率也就越高。若想将一个页中存放的记录数变多,可以启用压缩功能。

此外,启用压缩后,存储空间占用也变小了,同样单位的存储能存放的数据也变多了。

表压缩可以在创建表时开启,压缩表能够使表中的数据以压缩格式存储,压缩能够显著提高原生性能和可伸缩性。压缩意味着在硬盘和内存之间传输的数据更小且占用相对少的内存及硬盘,对于辅助索引,这种压缩带来更加明显的好处,因为索引数据也被压缩了。压缩对于硬盘是 SSD 的存储设备尤为重要,因为它们相对普通的 HDD 硬盘比较贵且容量有限。

我们都知道,CPU 和内存的速度远远大于磁盘,因为对于数据库服务器,磁盘 IO 可能会成为紧要资源或者瓶颈。数据压缩能够让数据库变得更小,从而减少磁盘的 I/O,还能提高系统吞吐量,以很小的成本(耗费较多的 CPU 资源)。对于读比重比较多的应用,压缩是特别有用。压缩能够让系统拥有足够的内存来存储热数据。

在创建InnoDB表时带上ROW_FORMAT=COMPRESSED参数能够使用比默认的 16K 更小的页。这样在读写时需要更少的 I/O,对于 SSD 磁盘更有价值。

COMPRESS 页压缩是MySQL5.7版本之前提供的页压缩功能。只要在创建表时指定ROW_FORMAT=COMPRESS,并设置通过选项KEY_BLOCK_SIZE设置压缩的比例。

CREATE TABLE `v_test2` (
+  `a` varchar(4) COLLATE utf8mb4_general_ci DEFAULT NULL,
+  `b` char(4) COLLATE utf8mb4_general_ci DEFAULT NULL,
+  `c` char(10) COLLATE utf8mb4_general_ci DEFAULT NULL,
+  `f` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8 ;
+

InnoDB 未压缩的数据页是 16KB,根据选项组合值,MySQL 为每个表的.ibd 文件使用 1KB,2KB,4KB,8kb,16KB 页大小。

实际的压缩算法并不会受 KEY_BLOCK_SIZE 值影响,这个值只是决定每个压缩块有多大,从而影响多少行被压缩到每个页。

设置 KEY_BLOCK_SIZE 值等于 16k 并不能有效的进行压缩,因为默认的 innodb 页就是 16k,但是对于拥有很多 BLOB,TEXT,VARCHAR 类型字段的表可能会有效果的。

参考

总结

到此,我已经将缓冲池 Buffer Pool介绍完毕了。

下面简单总结一下 Buffer Pool 从初始化到使用的整个流程。

1、MySQL 启动时会根据分配指定大小内存给 Buffer Pool,并且会创建一个个描述数据块和缓存页。

2、SQL 进来时,首先会根据数据的表空间和数据页编号查询 数据页缓存哈希表 中是否有对应的缓存页。

3、如果有对应的缓存页,则直接在 Buffer Pool中执行。

4、如果没有,则检查 free 链表看看有没有空闲的缓存页。

5、如果有空闲的缓存页,则从磁盘中加载对应的数据页,然后将描述数据块从 free 链表中移除,并且加入到 lru 链表的 old 数据区域的链表头部。后面如果被修改了,还需要加入到 flush 链表中。

6、如果没有空闲的缓存页,则将 lru 链表的 old 数据区域的链表尾部的缓存页刷回磁盘,然后清空,接着将数据页的数据加载到缓存页中,并且描述数据块会加入到 lru 链表的 old 数据区域的链表头部。后面如果被修改了,还需要加入到 flush 链表中。

7、5 或者 6 后,就接着在 Buffer Pool 中执行增删改查。

注意:5 和 6 中,缓存页加入到 old 数据区域的链表头部后,如果在 1s 后被访问,则将入到 new 数据区域的链表头部。

8、最后,就是描述数据块随着 SQL 语句的执行不断地在 free 链表、flush 链表和 lru 链表中移动了。

\ No newline at end of file diff --git "a/advanced/5.MySQL\346\272\220\344\273\243\347\240\201\350\260\203\350\257\225\347\216\257\345\242\203/index.html" "b/advanced/5.MySQL\346\272\220\344\273\243\347\240\201\350\260\203\350\257\225\347\216\257\345\242\203/index.html" new file mode 100644 index 000000000..6b039b818 --- /dev/null +++ "b/advanced/5.MySQL\346\272\220\344\273\243\347\240\201\350\260\203\350\257\225\347\216\257\345\242\203/index.html" @@ -0,0 +1,123 @@ + 5.搭建MySQL源代码调试debug环境 - MySQL WIKI
跳转至

概述

在如今开源数据库的时代,越来越多的人开始研究数据库的源码,并给社区贡献代码,MySQL 官方每次发布新版本都要感谢一些在社区上贡献代码的程序员。 现在新的数据库时代也给 DBA 提出了更高的要求,学会调试源码,通过源码定位问题,这是 DBA 进阶的方向。MySQL 的源码有几百上千万行,想全部搞懂几乎是不可能的,研究源码一般推荐从某个功能点入手。 而学会调试源码,不管对研究源码或通过源码定位问题,都是必备的技能。

一般在实际应用中,MySQL 都是运行在 Linux 平台下,在 Linux 平台下一般是通过 GDB(GNU symbolic debugger)工具进行调试,C/C++ 项目的开发和调试包括故障排查都是利用 GDB 完成的。

此外, VSCODE 这种 IDE 工具可以在本地的 Windows 操作系统下,通过 ssh 远程调试 Linux 平台下的 MySQL 。如果要在 Windows 上调试 Windows Vscode 插件

  • 中文语言包
  • Remote
  • C/C++
  • C/C++ Clang Command Adapter
  • CodeLLDB
  • CMake Tools

装完后,左侧会显示:分上下两栏。上栏是你本地 Windows 上装的 VSCode 插件;下栏是你远端 Linux 上装的 VSCode 插件。

  • 重要的事情提前说:mysql8.x 是天坑,编译时文件解压的空间强烈建议>40G!!!!30G 真得很勉强,成功全靠运气!编译等了 one hour,系统告诉你硬盘空间不足:)
  • 重要的事情提前说:请准备好足够的时间,我 8 核 16G 的服务器,编译了很长时间,大概是三个多小时。
  • 重要的事情提前说:编译异常后,需要删除对应的 cmake cache 后再次进行编译,否则每次都会读取缓存进行相同的报错。

源代码版本选择

首先需要从官网上下载源码,操作系统选择为 source code,操作系统版本选择为 ALL OPERATING SYSTEM,下载带 boost 头文件的源码包。如果对 MySQL 的版本没有特别要求的话,一般推荐下载最新版本的。 因为老版本中存在 bug 的概率较大,编译过程需要解决这些 bug,比如在 8.0.23 版本中编译过程中报了这个错:buf0buf.cc:1227:44: error: ‘SYS_gettid’ was not declared in this scope。 参考 MySQL 官方论坛:https://forums.mysql.com/read.php?117,674410,676378#msg-676378,在storage/innobase/buf/buf0flu.cc文件代码中加上声明#include <sys/syscall.h>,解决了这个报错。

环境配置

  • 硬件环境配置:8 核 + 16GiB + 200GiB 的服务器
  • 操作系统环境:Ubuntu 20.04.4 LTS
  • 软件环境
  • cmake version 3.16.3 ((Require 需要源码安装 cmake3.5.1+,但 cmake 不要升级到最新。 3.5.1 版本、3.5.2 版本即可。因为 3.20+版本编译 mysql8.0 会报各种错误)
  • GNU Make 4.2.1 (Require GNU make 3.75 or later GNU Make 4.2.1)
  • GCC gcc version 9.4.0 ( MySQL 8.4 source code permits use of C++17 features , Linux: GCC 10 )
# 准备环境
+apt install -y cmake make gcc g++ libncurses5-dev bison openssl libssl-dev git autoconf automake libtool  unzip build-essential perl pkg-config
+
+# 创建目录
+mkdir -p /data/{mysql_source_code,mysql_install_dir,mysql_data}  && cd /data/mysql_source_code
+
+# 直接去 https://dev.mysql.com/downloads/mysql/ 直接下载带 Boost 第三方库依赖的源码。
+# Boost 是一个功能强大、构造精巧、跨平台、开源并且完全免费的 C++ 程序库,可以认为是半个C++标准库。
+# MySQL 的代码依赖 Boost库,所以直接下载一个携带Boost库的源码比较省心,不需要再去下载对应的Boost库。
+wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-boost-8.0.39.tar.gz -P  /data/mysql_source_code
+
+# wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.39.tar.gz
+
+# 解压
+cd /data/mysql_source/  && tar -zxvf mysql-boost-8.0.39.tar.gz
+
+# 创建build目录并进入
+mkdir -p /data/mysql_source_code/mysql-8.0.39/build/ && cd /data/mysql_source_code/mysql-8.0.39/build/
+
+# Configure , 负责将源代码与当前系统进行配置和适配。
+
+cmake .. -DWITH_BOOST=/data/mysql_source_code/mysql-8.0.39/boost \
+-DWITH_DEBUG=1 \
+-DCMAKE_BUILD_TYPE=1\
+-DWITH_INNOBASE_STORAGE_ENGINE=1\
+-DWITH_ARCHIVE_STORAGE_ENGINE=1\
+-DWITH_BLACKHOLE_STORAGE_ENGINE=1\
+-DWITH_FEDERATED_STORAGE_ENGINE=1\
+-DWITH_PARTITION_STORAGE_ENGINE=1\
+-DMYSQL_TCP_PORT=3306\
+-DENABLED_LOCAL_INFILE=1\
+-DEXTRA_CHARSETS=all\
+-DEFAULT_CHARSET=utf8\
+-DDEFAULT_COLLATION=utf8_general_ci\
+-DMYSQL_USER=mysql\
+-DWITH_BINLOG_PREALLOC=ON\
+-DCMAKE_INSTALL_PREFIX=/data/mysql_install_dir
+
+# 参数含义
+# DWITH_DEBUG=1                         这个是最关键的配置,是为了开启debug调试模式;
+# DCMAKE_INSTALL_PREFIX=                表示编译状态的路径,选择源码文件夹之外的一个自建的build文件夹;
+# DWITH_BOOST=                          指定 boost 路径,可以直接指向源码文件夹下的boost文件夹;
+# DCMAKE_BUILD_TYPE=1                   表示开启debug,方便后续代码调试;
+# DWITH_BLACKHOLE_STORAGE_ENGINE=1      表示开启BLACKHOLE存储引擎
+# DWITH_PARTITION_STORAGE_ENGINE=1      表示开启PARTITION存储引擎
+# DWITH_FEDERATED_STORAGE_ENGINE=1      表示开启FEDERATED存储引擎
+# CMAKE_INSTALL_PREFIX=                 这个表示BASEDIR路径,默认是/usr/local/mysql,是各种配置的路径前缀PREFIX
+# DMYSQL_DATADIR:                      这个表示表示MySQL默认的数据目录,选择build文件夹下的data文件
+# 其他详细参数参考官网 https://dev.mysql.com/doc/refman/8.4/en/source-configuration-options.html
+# Cmake构建参数,主要分为几类:
+# 1. 通用参数:
+# 2. 安装布局参数:
+# 3. 存储参数:
+# 4. 特性参数:
+
+# 根据 Makefile 中的规则进行实际的编译过程,生成可执行文件或库。
+make -j4
+
+# 负责将最终编译好的文件复制到指定的安装目录中,以供系统中的其他程序使用。
+make install
+
+# 也可以使用make package来生成安装包(就像二进制包一样)
+
+
+# 接着make install成功后,配置一个简单的常规配置文件/etc/my.cnf,就可以初始化数据库并启动数据库了。
+
+
+
+/usr/local/mysql/bin/mysqld  --initialize-insecure
+
+mysqld_safe --user=mysql &
+
+
+# 启动完数据库后,登录数据库可以发现现在已经是debug模式了。
+/usr/local/mysql/bin/mysql -u root -p
+mysql> select version();
++--------------+
+| version()    |
++--------------+
+| 8.0.39-debug |
++--------------+
+1 row in set (0.00 sec)
+
+mysql>
+

修改MySQL版本

/include/mysql_version.h这是一个C语言的头文件,是在编译的过程中生成的,通过cmake和make之后就会生成。源代码目录中实际并不存在这个文件。

源代码实际上只有/include/mysql_version.h.in ,这种.h.in是一个模板文件,它是在cmake或者automake的过程中产生的一个用于输入设置信息等功能的中间文件。它会在你调用confing、automake等.sh文件之后,自动生成一个相应的.h文件,然后就可以在源码中调用。

# Bug #31466846 RENAME THE VERSION FILE TO MYSQL_VERSION
+
+version  C++11 的一个头文件,但 MySQL 以往都是在源代码中用 VERSION 这个文件来表示版本号,在引入文件时又因 macOS 不区分文件大小写,产生了冲突,导致编译时报错中断。所以后面改成了MYSQL_VERSION
+
  • ./mysql-8.0.39/include/mysql_version.h.in 源代码中的模版文件,这个文件没有硬编码,只是定义了一系列宏
  • ./mysql-8.0.39/MYSQL_VERSION 版本文件,定义了版本号:MYSQL_VERSION_MAJOR.MYSQL_VERSION_MINOR.MYSQL_VERSION_PATCH
  • ./mysql-8.0.39/build/include/mysql_version.h make之后生成的版本文件
  • ./mysql-8.0.39/cmake/mysql_server.cmake 版本构建脚本

https://dev.mysql.com/doc/dev/mysql-server/8.0.40/mysql__version_8h_source.html

https://cloud.tencent.com/developer/article/2223495

#define PROTOCOL_VERSION            10
+#define MYSQL_SERVER_VERSION       "8.0.39"
+#define MYSQL_BASE_VERSION         "mysqld-8.0"
+#define MYSQL_SERVER_SUFFIX_DEF    ""
+#define MYSQL_VERSION_ID            80039
+#define MYSQL_VERSION_STABILITY     "LTS"
+#define MYSQL_PORT                  3306
+#define MYSQL_ADMIN_PORT            33062
+#define MYSQL_PORT_DEFAULT          0
+#define MYSQL_UNIX_ADDR            "/tmp/mysql.sock"
+#define MYSQL_CONFIG_NAME          "my"
+#define MYSQL_PERSIST_CONFIG_NAME  "mysqld-auto"
+#define MYSQL_COMPILATION_COMMENT  "Source distribution"
+#define MYSQL_COMPILATION_COMMENT_SERVER  "Source distribution"
+#define LIBMYSQL_VERSION           "8.0.39"
+#define LIBMYSQL_VERSION_ID         80039
+

VSCODE REMOTE-SSH 工作原理

从官方介绍文档中的这张原理图我们可以看到,用户本地的VS Code是通过SSH通道连接到远程主机的。用户的开发代码、运行环境、调试环境都是在远程主机上。

远程连接是基于Visual Studio Code Remote - SSH这个扩展来实现的。

当用户进行远程连接时,VS Code会在远程主机上安装一个 server 包,这个安装过程是在首次连接时自动完成的。由于 Server 包是安装和运行在远端服务器上,而本地的 VS Code 只是编辑和展示的窗口,两者之间通过SSH Tunnel通信,因此实际的工作环境完全是在远端,如果需要使用第三方的扩展,也可以直接安装在远端服务器环境。

1、具体的连接过程如下:

首次连接时,下载VS Code ServerVS Code Server包的版本取决于你本地使用的VS Code的版本,下载地址为:

x86: https://update.code.visualstudio.com/commit:${commit_id}/server-linux-x64/stable
+arm: https://update.code.visualstudio.com/commit:${commit_id}/server-linux-arm64/stable
+

其中commit_id是变化的,每个不同版本的VS Codecommit_id都不同,可以在VS Code的 Help -> About 中查看

[client]
+port    = 3306
+socket = /usr/local/mysql/mysql.sock
+
+[mysql]
+prompt="\u@\h \R:\m:\s [\d]> "
+no-auto-rehash
+
+[mysqld]
+user    = root
+port    = 3306
+admin_address = 127.0.0.1
+basedir = /usr/local/mysql
+datadir = /data/mysql_data/
+socket    = /usr/local/mysql/mysql.sock
+pid-file = /usr/local/mysql/mysqld.pid
+character-set-server = utf8mb4
+
\ No newline at end of file diff --git "a/assets/GB T 12991.1-2008 \344\277\241\346\201\257\346\212\200\346\234\257 \346\225\260\346\215\256\345\272\223\350\257\255\350\250\200 SQL \347\254\2541\351\203\250\345\210\206_ \346\241\206\346\236\266.pdf" "b/assets/GB T 12991.1-2008 \344\277\241\346\201\257\346\212\200\346\234\257 \346\225\260\346\215\256\345\272\223\350\257\255\350\250\200 SQL \347\254\2541\351\203\250\345\210\206_ \346\241\206\346\236\266.pdf" new file mode 100644 index 000000000..aa15f85de Binary files /dev/null and "b/assets/GB T 12991.1-2008 \344\277\241\346\201\257\346\212\200\346\234\257 \346\225\260\346\215\256\345\272\223\350\257\255\350\250\200 SQL \347\254\2541\351\203\250\345\210\206_ \346\241\206\346\236\266.pdf" differ diff --git "a/assets/GB-T20273-2019\343\200\212\344\277\241\346\201\257\345\256\211\345\205\250\346\212\200\346\234\257\346\225\260\346\215\256\345\272\223\347\256\241\347\220\206\347\263\273\347\273\237\345\256\211\345\205\250\346\212\200\346\234\257\350\246\201\346\261\202\343\200\213.pdf" "b/assets/GB-T20273-2019\343\200\212\344\277\241\346\201\257\345\256\211\345\205\250\346\212\200\346\234\257\346\225\260\346\215\256\345\272\223\347\256\241\347\220\206\347\263\273\347\273\237\345\256\211\345\205\250\346\212\200\346\234\257\350\246\201\346\261\202\343\200\213.pdf" new file mode 100644 index 000000000..79a36b83b Binary files /dev/null and "b/assets/GB-T20273-2019\343\200\212\344\277\241\346\201\257\345\256\211\345\205\250\346\212\200\346\234\257\346\225\260\346\215\256\345\272\223\347\256\241\347\220\206\347\263\273\347\273\237\345\256\211\345\205\250\346\212\200\346\234\257\350\246\201\346\261\202\343\200\213.pdf" differ diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 000000000..1cf13b9f9 Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.1e8ae164.min.js b/assets/javascripts/bundle.1e8ae164.min.js new file mode 100644 index 000000000..212979889 --- /dev/null +++ b/assets/javascripts/bundle.1e8ae164.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var _i=Object.create;var br=Object.defineProperty;var Ai=Object.getOwnPropertyDescriptor;var Ci=Object.getOwnPropertyNames,Ft=Object.getOwnPropertySymbols,ki=Object.getPrototypeOf,vr=Object.prototype.hasOwnProperty,eo=Object.prototype.propertyIsEnumerable;var Zr=(e,t,r)=>t in e?br(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,F=(e,t)=>{for(var r in t||(t={}))vr.call(t,r)&&Zr(e,r,t[r]);if(Ft)for(var r of Ft(t))eo.call(t,r)&&Zr(e,r,t[r]);return e};var to=(e,t)=>{var r={};for(var o in e)vr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Ft)for(var o of Ft(e))t.indexOf(o)<0&&eo.call(e,o)&&(r[o]=e[o]);return r};var gr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Hi=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Ci(t))!vr.call(e,n)&&n!==r&&br(e,n,{get:()=>t[n],enumerable:!(o=Ai(t,n))||o.enumerable});return e};var jt=(e,t,r)=>(r=e!=null?_i(ki(e)):{},Hi(t||!e||!e.__esModule?br(r,"default",{value:e,enumerable:!0}):r,e));var ro=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{s(r.next(c))}catch(p){n(p)}},a=c=>{try{s(r.throw(c))}catch(p){n(p)}},s=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,a);s((r=r.apply(e,t)).next())});var no=gr((xr,oo)=>{(function(e,t){typeof xr=="object"&&typeof oo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(xr,function(){"use strict";function e(r){var o=!0,n=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(C){return!!(C&&C!==document&&C.nodeName!=="HTML"&&C.nodeName!=="BODY"&&"classList"in C&&"contains"in C.classList)}function c(C){var ct=C.type,Ne=C.tagName;return!!(Ne==="INPUT"&&a[ct]&&!C.readOnly||Ne==="TEXTAREA"&&!C.readOnly||C.isContentEditable)}function p(C){C.classList.contains("focus-visible")||(C.classList.add("focus-visible"),C.setAttribute("data-focus-visible-added",""))}function l(C){C.hasAttribute("data-focus-visible-added")&&(C.classList.remove("focus-visible"),C.removeAttribute("data-focus-visible-added"))}function f(C){C.metaKey||C.altKey||C.ctrlKey||(s(r.activeElement)&&p(r.activeElement),o=!0)}function u(C){o=!1}function h(C){s(C.target)&&(o||c(C.target))&&p(C.target)}function w(C){s(C.target)&&(C.target.classList.contains("focus-visible")||C.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(C.target))}function A(C){document.visibilityState==="hidden"&&(n&&(o=!0),Z())}function Z(){document.addEventListener("mousemove",J),document.addEventListener("mousedown",J),document.addEventListener("mouseup",J),document.addEventListener("pointermove",J),document.addEventListener("pointerdown",J),document.addEventListener("pointerup",J),document.addEventListener("touchmove",J),document.addEventListener("touchstart",J),document.addEventListener("touchend",J)}function te(){document.removeEventListener("mousemove",J),document.removeEventListener("mousedown",J),document.removeEventListener("mouseup",J),document.removeEventListener("pointermove",J),document.removeEventListener("pointerdown",J),document.removeEventListener("pointerup",J),document.removeEventListener("touchmove",J),document.removeEventListener("touchstart",J),document.removeEventListener("touchend",J)}function J(C){C.target.nodeName&&C.target.nodeName.toLowerCase()==="html"||(o=!1,te())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",A,!0),Z(),r.addEventListener("focus",h,!0),r.addEventListener("blur",w,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var zr=gr((kt,Vr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof kt=="object"&&typeof Vr=="object"?Vr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof kt=="object"?kt.ClipboardJS=r():t.ClipboardJS=r()})(kt,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return Li}});var a=i(279),s=i.n(a),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(D){try{return document.execCommand(D)}catch(M){return!1}}var h=function(M){var O=f()(M);return u("cut"),O},w=h;function A(D){var M=document.documentElement.getAttribute("dir")==="rtl",O=document.createElement("textarea");O.style.fontSize="12pt",O.style.border="0",O.style.padding="0",O.style.margin="0",O.style.position="absolute",O.style[M?"right":"left"]="-9999px";var I=window.pageYOffset||document.documentElement.scrollTop;return O.style.top="".concat(I,"px"),O.setAttribute("readonly",""),O.value=D,O}var Z=function(M,O){var I=A(M);O.container.appendChild(I);var W=f()(I);return u("copy"),I.remove(),W},te=function(M){var O=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},I="";return typeof M=="string"?I=Z(M,O):M instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(M==null?void 0:M.type)?I=Z(M.value,O):(I=f()(M),u("copy")),I},J=te;function C(D){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?C=function(O){return typeof O}:C=function(O){return O&&typeof Symbol=="function"&&O.constructor===Symbol&&O!==Symbol.prototype?"symbol":typeof O},C(D)}var ct=function(){var M=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},O=M.action,I=O===void 0?"copy":O,W=M.container,K=M.target,Ce=M.text;if(I!=="copy"&&I!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(K!==void 0)if(K&&C(K)==="object"&&K.nodeType===1){if(I==="copy"&&K.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(I==="cut"&&(K.hasAttribute("readonly")||K.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Ce)return J(Ce,{container:W});if(K)return I==="cut"?w(K):J(K,{container:W})},Ne=ct;function Pe(D){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Pe=function(O){return typeof O}:Pe=function(O){return O&&typeof Symbol=="function"&&O.constructor===Symbol&&O!==Symbol.prototype?"symbol":typeof O},Pe(D)}function xi(D,M){if(!(D instanceof M))throw new TypeError("Cannot call a class as a function")}function Xr(D,M){for(var O=0;O0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof W.action=="function"?W.action:this.defaultAction,this.target=typeof W.target=="function"?W.target:this.defaultTarget,this.text=typeof W.text=="function"?W.text:this.defaultText,this.container=Pe(W.container)==="object"?W.container:document.body}},{key:"listenClick",value:function(W){var K=this;this.listener=p()(W,"click",function(Ce){return K.onClick(Ce)})}},{key:"onClick",value:function(W){var K=W.delegateTarget||W.currentTarget,Ce=this.action(K)||"copy",It=Ne({action:Ce,container:this.container,target:this.target(K),text:this.text(K)});this.emit(It?"success":"error",{action:Ce,text:It,trigger:K,clearSelection:function(){K&&K.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(W){return hr("action",W)}},{key:"defaultTarget",value:function(W){var K=hr("target",W);if(K)return document.querySelector(K)}},{key:"defaultText",value:function(W){return hr("text",W)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(W){var K=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return J(W,K)}},{key:"cut",value:function(W){return w(W)}},{key:"isSupported",value:function(){var W=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],K=typeof W=="string"?[W]:W,Ce=!!document.queryCommandSupported;return K.forEach(function(It){Ce=Ce&&!!document.queryCommandSupported(It)}),Ce}}]),O}(s()),Li=Mi},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,c){for(;s&&s.nodeType!==n;){if(typeof s.matches=="function"&&s.matches(c))return s;s=s.parentNode}}o.exports=a},438:function(o,n,i){var a=i(828);function s(l,f,u,h,w){var A=p.apply(this,arguments);return l.addEventListener(u,A,w),{destroy:function(){l.removeEventListener(u,A,w)}}}function c(l,f,u,h,w){return typeof l.addEventListener=="function"?s.apply(null,arguments):typeof u=="function"?s.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(A){return s(A,f,u,h,w)}))}function p(l,f,u,h){return function(w){w.delegateTarget=a(w.target,f),w.delegateTarget&&h.call(l,w)}}o.exports=c},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(o,n,i){var a=i(879),s=i(438);function c(u,h,w){if(!u&&!h&&!w)throw new Error("Missing required arguments");if(!a.string(h))throw new TypeError("Second argument must be a String");if(!a.fn(w))throw new TypeError("Third argument must be a Function");if(a.node(u))return p(u,h,w);if(a.nodeList(u))return l(u,h,w);if(a.string(u))return f(u,h,w);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,h,w){return u.addEventListener(h,w),{destroy:function(){u.removeEventListener(h,w)}}}function l(u,h,w){return Array.prototype.forEach.call(u,function(A){A.addEventListener(h,w)}),{destroy:function(){Array.prototype.forEach.call(u,function(A){A.removeEventListener(h,w)})}}}function f(u,h,w){return s(document.body,u,h,w)}o.exports=c},817:function(o){function n(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),a=c.toString()}return a}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,a,s){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var c=this;function p(){c.off(i,p),a.apply(s,arguments)}return p._=a,this.on(i,p,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=s.length;for(c;c{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var Va=/["'&<>]/;qn.exports=za;function za(e){var t=""+e,r=Va.exec(t);if(!r)return t;var o,n="",i=0,a=0;for(i=r.index;i0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function V(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],a;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(s){a={error:s}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(a)throw a.error}}return i}function z(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||s(u,h)})})}function s(u,h){try{c(o[u](h))}catch(w){f(i[0][3],w)}}function c(u){u.value instanceof ot?Promise.resolve(u.value.v).then(p,l):f(i[0][2],u)}function p(u){s("next",u)}function l(u){s("throw",u)}function f(u,h){u(h),i.shift(),i.length&&s(i[0][0],i[0][1])}}function so(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof ue=="function"?ue(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(a){return new Promise(function(s,c){a=e[i](a),n(s,c,a.done,a.value)})}}function n(i,a,s,c){Promise.resolve(c).then(function(p){i({value:p,done:s})},a)}}function k(e){return typeof e=="function"}function pt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Wt=pt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=ue(a),c=s.next();!c.done;c=s.next()){var p=c.value;p.remove(this)}}catch(A){t={error:A}}finally{try{c&&!c.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var l=this.initialTeardown;if(k(l))try{l()}catch(A){i=A instanceof Wt?A.errors:[A]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=ue(f),h=u.next();!h.done;h=u.next()){var w=h.value;try{co(w)}catch(A){i=i!=null?i:[],A instanceof Wt?i=z(z([],V(i)),V(A.errors)):i.push(A)}}}catch(A){o={error:A}}finally{try{h&&!h.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Wt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)co(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Er=Ie.EMPTY;function Dt(e){return e instanceof Ie||e&&"closed"in e&&k(e.remove)&&k(e.add)&&k(e.unsubscribe)}function co(e){k(e)?e():e.unsubscribe()}var ke={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var lt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,a=n.isStopped,s=n.observers;return i||a?Er:(this.currentObservers=null,s.push(r),new Ie(function(){o.currentObservers=null,Ve(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,a=o.isStopped;n?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new j;return r.source=this,r},t.create=function(r,o){return new vo(r,o)},t}(j);var vo=function(e){se(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:Er},t}(g);var St={now:function(){return(St.delegate||Date).now()},delegate:void 0};var Ot=function(e){se(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=St);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,a=o._infiniteTimeWindow,s=o._timestampProvider,c=o._windowTime;n||(i.push(r),!a&&i.push(s.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,a=n._buffer,s=a.slice(),c=0;c0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var a=r.actions;o!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==o&&(ut.cancelAnimationFrame(o),r._scheduled=void 0)},t}(zt);var yo=function(e){se(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(qt);var de=new yo(xo);var L=new j(function(e){return e.complete()});function Kt(e){return e&&k(e.schedule)}function _r(e){return e[e.length-1]}function Je(e){return k(_r(e))?e.pop():void 0}function Ae(e){return Kt(_r(e))?e.pop():void 0}function Qt(e,t){return typeof _r(e)=="number"?e.pop():t}var dt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Yt(e){return k(e==null?void 0:e.then)}function Bt(e){return k(e[ft])}function Gt(e){return Symbol.asyncIterator&&k(e==null?void 0:e[Symbol.asyncIterator])}function Jt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Di(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Xt=Di();function Zt(e){return k(e==null?void 0:e[Xt])}function er(e){return ao(this,arguments,function(){var r,o,n,i;return Ut(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,ot(r.read())];case 3:return o=a.sent(),n=o.value,i=o.done,i?[4,ot(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,ot(n)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function tr(e){return k(e==null?void 0:e.getReader)}function N(e){if(e instanceof j)return e;if(e!=null){if(Bt(e))return Ni(e);if(dt(e))return Vi(e);if(Yt(e))return zi(e);if(Gt(e))return Eo(e);if(Zt(e))return qi(e);if(tr(e))return Ki(e)}throw Jt(e)}function Ni(e){return new j(function(t){var r=e[ft]();if(k(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Vi(e){return new j(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?b(function(n,i){return e(n,i,o)}):ce,ye(1),r?Qe(t):jo(function(){return new or}))}}function $r(e){return e<=0?function(){return L}:x(function(t,r){var o=[];t.subscribe(S(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new g}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,c=s===void 0?!0:s;return function(p){var l,f,u,h=0,w=!1,A=!1,Z=function(){f==null||f.unsubscribe(),f=void 0},te=function(){Z(),l=u=void 0,w=A=!1},J=function(){var C=l;te(),C==null||C.unsubscribe()};return x(function(C,ct){h++,!A&&!w&&Z();var Ne=u=u!=null?u:r();ct.add(function(){h--,h===0&&!A&&!w&&(f=Pr(J,c))}),Ne.subscribe(ct),!l&&h>0&&(l=new it({next:function(Pe){return Ne.next(Pe)},error:function(Pe){A=!0,Z(),f=Pr(te,n,Pe),Ne.error(Pe)},complete:function(){w=!0,Z(),f=Pr(te,a),Ne.complete()}}),N(C).subscribe(l))})(p)}}function Pr(e,t){for(var r=[],o=2;oe.next(document)),e}function R(e,t=document){return Array.from(t.querySelectorAll(e))}function P(e,t=document){let r=me(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function me(e,t=document){return t.querySelector(e)||void 0}function Re(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var la=T(d(document.body,"focusin"),d(document.body,"focusout")).pipe(be(1),q(void 0),m(()=>Re()||document.body),B(1));function vt(e){return la.pipe(m(t=>e.contains(t)),Y())}function Vo(e,t){return T(d(e,"mouseenter").pipe(m(()=>!0)),d(e,"mouseleave").pipe(m(()=>!1))).pipe(t?be(t):ce,q(!1))}function Ue(e){return{x:e.offsetLeft,y:e.offsetTop}}function zo(e){return T(d(window,"load"),d(window,"resize")).pipe(Me(0,de),m(()=>Ue(e)),q(Ue(e)))}function ir(e){return{x:e.scrollLeft,y:e.scrollTop}}function et(e){return T(d(e,"scroll"),d(window,"resize")).pipe(Me(0,de),m(()=>ir(e)),q(ir(e)))}function qo(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)qo(e,r)}function E(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)qo(o,n);return o}function ar(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function gt(e){let t=E("script",{src:e});return H(()=>(document.head.appendChild(t),T(d(t,"load"),d(t,"error").pipe(v(()=>Ar(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),_(()=>document.head.removeChild(t)),ye(1))))}var Ko=new g,ma=H(()=>typeof ResizeObserver=="undefined"?gt("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>{for(let t of e)Ko.next(t)})),v(e=>T(qe,$(e)).pipe(_(()=>e.disconnect()))),B(1));function pe(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Ee(e){return ma.pipe(y(t=>t.observe(e)),v(t=>Ko.pipe(b(({target:r})=>r===e),_(()=>t.unobserve(e)),m(()=>pe(e)))),q(pe(e)))}function xt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function sr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var Qo=new g,fa=H(()=>$(new IntersectionObserver(e=>{for(let t of e)Qo.next(t)},{threshold:0}))).pipe(v(e=>T(qe,$(e)).pipe(_(()=>e.disconnect()))),B(1));function yt(e){return fa.pipe(y(t=>t.observe(e)),v(t=>Qo.pipe(b(({target:r})=>r===e),_(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function Yo(e,t=16){return et(e).pipe(m(({y:r})=>{let o=pe(e),n=xt(e);return r>=n.height-o.height-t}),Y())}var cr={drawer:P("[data-md-toggle=drawer]"),search:P("[data-md-toggle=search]")};function Bo(e){return cr[e].checked}function Be(e,t){cr[e].checked!==t&&cr[e].click()}function We(e){let t=cr[e];return d(t,"change").pipe(m(()=>t.checked),q(t.checked))}function ua(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function da(){return T(d(window,"compositionstart").pipe(m(()=>!0)),d(window,"compositionend").pipe(m(()=>!1))).pipe(q(!1))}function Go(){let e=d(window,"keydown").pipe(b(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:Bo("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),b(({mode:t,type:r})=>{if(t==="global"){let o=Re();if(typeof o!="undefined")return!ua(o,r)}return!0}),le());return da().pipe(v(t=>t?L:e))}function ve(){return new URL(location.href)}function st(e,t=!1){if(G("navigation.instant")&&!t){let r=E("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function Jo(){return new g}function Xo(){return location.hash.slice(1)}function Zo(e){let t=E("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function ha(e){return T(d(window,"hashchange"),e).pipe(m(Xo),q(Xo()),b(t=>t.length>0),B(1))}function en(e){return ha(e).pipe(m(t=>me(`[id="${t}"]`)),b(t=>typeof t!="undefined"))}function At(e){let t=matchMedia(e);return nr(r=>t.addListener(()=>r(t.matches))).pipe(q(t.matches))}function tn(){let e=matchMedia("print");return T(d(window,"beforeprint").pipe(m(()=>!0)),d(window,"afterprint").pipe(m(()=>!1))).pipe(q(e.matches))}function Ur(e,t){return e.pipe(v(r=>r?t():L))}function Wr(e,t){return new j(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let a=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+a*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function De(e,t){return Wr(e,t).pipe(v(r=>r.text()),m(r=>JSON.parse(r)),B(1))}function rn(e,t){let r=new DOMParser;return Wr(e,t).pipe(v(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),B(1))}function on(e,t){let r=new DOMParser;return Wr(e,t).pipe(v(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),B(1))}function nn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function an(){return T(d(window,"scroll",{passive:!0}),d(window,"resize",{passive:!0})).pipe(m(nn),q(nn()))}function sn(){return{width:innerWidth,height:innerHeight}}function cn(){return d(window,"resize",{passive:!0}).pipe(m(sn),q(sn()))}function pn(){return Q([an(),cn()]).pipe(m(([e,t])=>({offset:e,size:t})),B(1))}function pr(e,{viewport$:t,header$:r}){let o=t.pipe(X("size")),n=Q([o,r]).pipe(m(()=>Ue(e)));return Q([r,t,n]).pipe(m(([{height:i},{offset:a,size:s},{x:c,y:p}])=>({offset:{x:a.x-c,y:a.y-p+i},size:s})))}function ba(e){return d(e,"message",t=>t.data)}function va(e){let t=new g;return t.subscribe(r=>e.postMessage(r)),t}function ln(e,t=new Worker(e)){let r=ba(t),o=va(t),n=new g;n.subscribe(o);let i=o.pipe(ee(),oe(!0));return n.pipe(ee(),$e(r.pipe(U(i))),le())}var ga=P("#__config"),Et=JSON.parse(ga.textContent);Et.base=`${new URL(Et.base,ve())}`;function we(){return Et}function G(e){return Et.features.includes(e)}function ge(e,t){return typeof t!="undefined"?Et.translations[e].replace("#",t.toString()):Et.translations[e]}function Te(e,t=document){return P(`[data-md-component=${e}]`,t)}function ie(e,t=document){return R(`[data-md-component=${e}]`,t)}function xa(e){let t=P(".md-typeset > :first-child",e);return d(t,"click",{once:!0}).pipe(m(()=>P(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function mn(e){if(!G("announce.dismiss")||!e.childElementCount)return L;if(!e.hidden){let t=P(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new g;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),xa(e).pipe(y(r=>t.next(r)),_(()=>t.complete()),m(r=>F({ref:e},r)))})}function ya(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function fn(e,t){let r=new g;return r.subscribe(({hidden:o})=>{e.hidden=o}),ya(e,t).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))}function Ct(e,t){return t==="inline"?E("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},E("div",{class:"md-tooltip__inner md-typeset"})):E("div",{class:"md-tooltip",id:e,role:"tooltip"},E("div",{class:"md-tooltip__inner md-typeset"}))}function un(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return E("aside",{class:"md-annotation",tabIndex:0},Ct(t),E("a",{href:r,class:"md-annotation__index",tabIndex:-1},E("span",{"data-md-annotation-id":e})))}else return E("aside",{class:"md-annotation",tabIndex:0},Ct(t),E("span",{class:"md-annotation__index",tabIndex:-1},E("span",{"data-md-annotation-id":e})))}function dn(e){return E("button",{class:"md-clipboard md-icon",title:ge("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}function Dr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,E("del",null,p)," "],[]).slice(0,-1),i=we(),a=new URL(e.location,i.base);G("search.highlight")&&a.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:s}=we();return E("a",{href:`${a}`,class:"md-search-result__link",tabIndex:-1},E("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&E("div",{class:"md-search-result__icon md-icon"}),r>0&&E("h1",null,e.title),r<=0&&E("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&e.tags.map(c=>{let p=s?c in s?`md-tag-icon md-tag--${s[c]}`:"md-tag-icon":"";return E("span",{class:`md-tag ${p}`},c)}),o>0&&n.length>0&&E("p",{class:"md-search-result__terms"},ge("search.result.term.missing"),": ",...n)))}function hn(e){let t=e[0].score,r=[...e],o=we(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),a=r.findIndex(l=>l.scoreDr(l,1)),...c.length?[E("details",{class:"md-search-result__more"},E("summary",{tabIndex:-1},E("div",null,c.length>0&&c.length===1?ge("search.result.more.one"):ge("search.result.more.other",c.length))),...c.map(l=>Dr(l,1)))]:[]];return E("li",{class:"md-search-result__item"},p)}function bn(e){return E("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>E("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?ar(r):r)))}function Nr(e){let t=`tabbed-control tabbed-control--${e}`;return E("div",{class:t,hidden:!0},E("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function vn(e){return E("div",{class:"md-typeset__scrollwrap"},E("div",{class:"md-typeset__table"},e))}function Ea(e){let t=we(),r=new URL(`../${e.version}/`,t.base);return E("li",{class:"md-version__item"},E("a",{href:`${r}`,class:"md-version__link"},e.title))}function gn(e,t){return e=e.filter(r=>{var o;return!((o=r.properties)!=null&&o.hidden)}),E("div",{class:"md-version"},E("button",{class:"md-version__current","aria-label":ge("select.version")},t.title),E("ul",{class:"md-version__list"},e.map(Ea)))}var wa=0;function Ta(e,t){document.body.append(e);let{width:r}=pe(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=sr(t),n=typeof o!="undefined"?et(o):$({x:0,y:0}),i=T(vt(t),Vo(t)).pipe(Y());return Q([i,n]).pipe(m(([a,s])=>{let{x:c,y:p}=Ue(t),l=pe(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:a,offset:{x:c-s.x+l.width/2-r/2,y:p-s.y+l.height+8}}}))}function Ge(e){let t=e.title;if(!t.length)return L;let r=`__tooltip_${wa++}`,o=Ct(r,"inline"),n=P(".md-typeset",o);return n.innerHTML=t,H(()=>{let i=new g;return i.subscribe({next({offset:a}){o.style.setProperty("--md-tooltip-x",`${a.x}px`),o.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),T(i.pipe(b(({active:a})=>a)),i.pipe(be(250),b(({active:a})=>!a))).subscribe({next({active:a}){a?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe(Me(16,de)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(_t(125,de),b(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?o.style.setProperty("--md-tooltip-0",`${-a}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),Ta(o,e).pipe(y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))}).pipe(ze(ae))}function Sa(e,t){let r=H(()=>Q([zo(e),et(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:a,height:s}=pe(e);return{x:o-i.x+a/2,y:n-i.y+s/2}}));return vt(e).pipe(v(o=>r.pipe(m(n=>({active:o,offset:n})),ye(+!o||1/0))))}function xn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new g,a=i.pipe(ee(),oe(!0));return i.subscribe({next({offset:s}){e.style.setProperty("--md-tooltip-x",`${s.x}px`),e.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),yt(e).pipe(U(a)).subscribe(s=>{e.toggleAttribute("data-md-visible",s)}),T(i.pipe(b(({active:s})=>s)),i.pipe(be(250),b(({active:s})=>!s))).subscribe({next({active:s}){s?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Me(16,de)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(_t(125,de),b(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?e.style.setProperty("--md-tooltip-0",`${-s}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),d(n,"click").pipe(U(a),b(s=>!(s.metaKey||s.ctrlKey))).subscribe(s=>{s.stopPropagation(),s.preventDefault()}),d(n,"mousedown").pipe(U(a),ne(i)).subscribe(([s,{active:c}])=>{var p;if(s.button!==0||s.metaKey||s.ctrlKey)s.preventDefault();else if(c){s.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Re())==null||p.blur()}}),r.pipe(U(a),b(s=>s===o),Ye(125)).subscribe(()=>e.focus()),Sa(e,t).pipe(y(s=>i.next(s)),_(()=>i.complete()),m(s=>F({ref:e},s)))})}function Oa(e){return e.tagName==="CODE"?R(".c, .c1, .cm",e):[e]}function Ma(e){let t=[];for(let r of Oa(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let a;for(;a=/(\(\d+\))(!)?/.exec(i.textContent);){let[,s,c]=a;if(typeof c=="undefined"){let p=i.splitText(a.index);i=p.splitText(s.length),t.push(p)}else{i.textContent=s,t.push(i);break}}}}return t}function yn(e,t){t.append(...Array.from(e.childNodes))}function lr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,a=new Map;for(let s of Ma(t)){let[,c]=s.textContent.match(/\((\d+)\)/);me(`:scope > li:nth-child(${c})`,e)&&(a.set(c,un(c,i)),s.replaceWith(a.get(c)))}return a.size===0?L:H(()=>{let s=new g,c=s.pipe(ee(),oe(!0)),p=[];for(let[l,f]of a)p.push([P(".md-typeset",f),P(`:scope > li:nth-child(${l})`,e)]);return o.pipe(U(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?yn(f,u):yn(u,f)}),T(...[...a].map(([,l])=>xn(l,t,{target$:r}))).pipe(_(()=>s.complete()),le())})}function En(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return En(t)}}function wn(e,t){return H(()=>{let r=En(e);return typeof r!="undefined"?lr(r,e,t):L})}var Tn=jt(zr());var La=0;function Sn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Sn(t)}}function _a(e){return Ee(e).pipe(m(({width:t})=>({scrollable:xt(e).width>t})),X("scrollable"))}function On(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new g,i=n.pipe($r(1));n.subscribe(({scrollable:c})=>{c&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let a=[];if(Tn.default.isSupported()&&(e.closest(".copy")||G("content.code.copy")&&!e.closest(".no-copy"))){let c=e.closest("pre");c.id=`__code_${La++}`;let p=dn(c.id);c.insertBefore(p,e),G("content.tooltips")&&a.push(Ge(p))}let s=e.closest(".highlight");if(s instanceof HTMLElement){let c=Sn(s);if(typeof c!="undefined"&&(s.classList.contains("annotate")||G("content.code.annotate"))){let p=lr(c,e,t);a.push(Ee(s).pipe(U(i),m(({width:l,height:f})=>l&&f),Y(),v(l=>l?p:L)))}}return _a(e).pipe(y(c=>n.next(c)),_(()=>n.complete()),m(c=>F({ref:e},c)),$e(...a))});return G("content.lazy")?yt(e).pipe(b(n=>n),ye(1),v(()=>o)):o}function Aa(e,{target$:t,print$:r}){let o=!0;return T(t.pipe(m(n=>n.closest("details:not([open])")),b(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(b(n=>n||!o),y(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Mn(e,t){return H(()=>{let r=new g;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),Aa(e,t).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}var Ln=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel rect,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel rect{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var qr,ka=0;function Ha(){return typeof mermaid=="undefined"||mermaid instanceof Element?gt("https://unpkg.com/mermaid@10.7.0/dist/mermaid.min.js"):$(void 0)}function _n(e){return e.classList.remove("mermaid"),qr||(qr=Ha().pipe(y(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Ln,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),B(1))),qr.subscribe(()=>ro(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${ka++}`,r=E("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),a=r.attachShadow({mode:"closed"});a.innerHTML=n,e.replaceWith(r),i==null||i(a)})),qr.pipe(m(()=>({ref:e})))}var An=E("table");function Cn(e){return e.replaceWith(An),An.replaceWith(vn(e)),$({ref:e})}function $a(e){let t=e.find(r=>r.checked)||e[0];return T(...e.map(r=>d(r,"change").pipe(m(()=>P(`label[for="${r.id}"]`))))).pipe(q(P(`label[for="${t.id}"]`)),m(r=>({active:r})))}function kn(e,{viewport$:t,target$:r}){let o=P(".tabbed-labels",e),n=R(":scope > input",e),i=Nr("prev");e.append(i);let a=Nr("next");return e.append(a),H(()=>{let s=new g,c=s.pipe(ee(),oe(!0));Q([s,Ee(e)]).pipe(U(c),Me(1,de)).subscribe({next([{active:p},l]){let f=Ue(p),{width:u}=pe(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let h=ir(o);(f.xh.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),Q([et(o),Ee(o)]).pipe(U(c)).subscribe(([p,l])=>{let f=xt(o);i.hidden=p.x<16,a.hidden=p.x>f.width-l.width-16}),T(d(i,"click").pipe(m(()=>-1)),d(a,"click").pipe(m(()=>1))).pipe(U(c)).subscribe(p=>{let{width:l}=pe(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(U(c),b(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=P(`label[for="${p.id}"]`);l.replaceChildren(E("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),d(l.firstElementChild,"click").pipe(U(c),b(f=>!(f.metaKey||f.ctrlKey)),y(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return G("content.tabs.link")&&s.pipe(Le(1),ne(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let w of R("[data-tabs]"))for(let A of R(":scope > input",w)){let Z=P(`label[for="${A.id}"]`);if(Z!==p&&Z.innerText.trim()===f){Z.setAttribute("data-md-switching",""),A.click();break}}window.scrollTo({top:e.offsetTop-u});let h=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...h])])}}),s.pipe(U(c)).subscribe(()=>{for(let p of R("audio, video",e))p.pause()}),$a(n).pipe(y(p=>s.next(p)),_(()=>s.complete()),m(p=>F({ref:e},p)))}).pipe(ze(ae))}function Hn(e,{viewport$:t,target$:r,print$:o}){return T(...R(".annotate:not(.highlight)",e).map(n=>wn(n,{target$:r,print$:o})),...R("pre:not(.mermaid) > code",e).map(n=>On(n,{target$:r,print$:o})),...R("pre.mermaid",e).map(n=>_n(n)),...R("table:not([class])",e).map(n=>Cn(n)),...R("details",e).map(n=>Mn(n,{target$:r,print$:o})),...R("[data-tabs]",e).map(n=>kn(n,{viewport$:t,target$:r})),...R("[title]",e).filter(()=>G("content.tooltips")).map(n=>Ge(n)))}function Ra(e,{alert$:t}){return t.pipe(v(r=>T($(!0),$(!1).pipe(Ye(2e3))).pipe(m(o=>({message:r,active:o})))))}function $n(e,t){let r=P(".md-typeset",e);return H(()=>{let o=new g;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),Ra(e,t).pipe(y(n=>o.next(n)),_(()=>o.complete()),m(n=>F({ref:e},n)))})}function Pa({viewport$:e}){if(!G("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),Ke(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=We("search");return Q([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),v(n=>n?r:$(!1)),q(!1))}function Rn(e,t){return H(()=>Q([Ee(e),Pa(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),B(1))}function Pn(e,{header$:t,main$:r}){return H(()=>{let o=new g,n=o.pipe(ee(),oe(!0));o.pipe(X("active"),je(t)).subscribe(([{active:a},{hidden:s}])=>{e.classList.toggle("md-header--shadow",a&&!s),e.hidden=s});let i=fe(R("[title]",e)).pipe(b(()=>G("content.tooltips")),re(a=>Ge(a)));return r.subscribe(o),t.pipe(U(n),m(a=>F({ref:e},a)),$e(i.pipe(U(n))))})}function Ia(e,{viewport$:t,header$:r}){return pr(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=pe(e);return{active:o>=n}}),X("active"))}function In(e,t){return H(()=>{let r=new g;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=me(".md-content h1");return typeof o=="undefined"?L:Ia(o,t).pipe(y(n=>r.next(n)),_(()=>r.complete()),m(n=>F({ref:e},n)))})}function Fn(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(v(()=>Ee(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),X("bottom"))));return Q([o,n,t]).pipe(m(([i,{top:a,bottom:s},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,a-c,i)-Math.max(0,p+c-s)),{offset:a-i,height:p,active:a-i<=c})),Y((i,a)=>i.offset===a.offset&&i.height===a.height&&i.active===a.active))}function Fa(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(re(o=>d(o,"change").pipe(m(()=>o))),q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),B(1))}function jn(e){let t=R("input",e),r=E("meta",{name:"theme-color"});document.head.appendChild(r);let o=E("meta",{name:"color-scheme"});document.head.appendChild(o);let n=At("(prefers-color-scheme: light)");return H(()=>{let i=new g;return i.subscribe(a=>{if(document.body.setAttribute("data-md-color-switching",""),a.color.media==="(prefers-color-scheme)"){let s=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(s.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");a.color.scheme=c.getAttribute("data-md-color-scheme"),a.color.primary=c.getAttribute("data-md-color-primary"),a.color.accent=c.getAttribute("data-md-color-accent")}for(let[s,c]of Object.entries(a.color))document.body.setAttribute(`data-md-color-${s}`,c);for(let s=0;sa.key==="Enter"),ne(i,(a,s)=>s)).subscribe(({index:a})=>{a=(a+1)%t.length,t[a].click(),t[a].focus()}),i.pipe(m(()=>{let a=Te("header"),s=window.getComputedStyle(a);return o.content=s.colorScheme,s.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(a=>r.content=`#${a}`),i.pipe(Oe(ae)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),Fa(t).pipe(U(n.pipe(Le(1))),at(),y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))})}function Un(e,{progress$:t}){return H(()=>{let r=new g;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(y(o=>r.next({value:o})),_(()=>r.complete()),m(o=>({ref:e,value:o})))})}var Kr=jt(zr());function ja(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function Wn({alert$:e}){Kr.default.isSupported()&&new j(t=>{new Kr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||ja(P(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(y(t=>{t.trigger.focus()}),m(()=>ge("clipboard.copied"))).subscribe(e)}function Dn(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function Ua(e,t){let r=new Map;for(let o of R("url",e)){let n=P("loc",o),i=[Dn(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let a of R("[rel=alternate]",o)){let s=a.getAttribute("href");s!=null&&i.push(Dn(new URL(s),t))}}return r}function mr(e){return on(new URL("sitemap.xml",e)).pipe(m(t=>Ua(t,new URL(e))),he(()=>$(new Map)))}function Wa(e,t){if(!(e.target instanceof Element))return L;let r=e.target.closest("a");if(r===null)return L;if(r.target||e.metaKey||e.ctrlKey)return L;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(new URL(r.href))):L}function Nn(e){let t=new Map;for(let r of R(":scope > *",e.head))t.set(r.outerHTML,r);return t}function Vn(e){for(let t of R("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function Da(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...G("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=me(o),i=me(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=Nn(document);for(let[o,n]of Nn(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Te("container");return Fe(R("script",r)).pipe(v(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new j(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),L}),ee(),oe(document))}function zn({location$:e,viewport$:t,progress$:r}){let o=we();if(location.protocol==="file:")return L;let n=mr(o.base);$(document).subscribe(Vn);let i=d(document.body,"click").pipe(je(n),v(([c,p])=>Wa(c,p)),le()),a=d(window,"popstate").pipe(m(ve),le());i.pipe(ne(t)).subscribe(([c,{offset:p}])=>{history.replaceState(p,""),history.pushState(null,"",c)}),T(i,a).subscribe(e);let s=e.pipe(X("pathname"),v(c=>rn(c,{progress$:r}).pipe(he(()=>(st(c,!0),L)))),v(Vn),v(Da),le());return T(s.pipe(ne(e,(c,p)=>p)),e.pipe(X("pathname"),v(()=>e),X("hash")),e.pipe(Y((c,p)=>c.pathname===p.pathname&&c.hash===p.hash),v(()=>i),y(()=>history.back()))).subscribe(c=>{var p,l;history.state!==null||!c.hash?window.scrollTo(0,(l=(p=history.state)==null?void 0:p.y)!=null?l:0):(history.scrollRestoration="auto",Zo(c.hash),history.scrollRestoration="manual")}),e.subscribe(()=>{history.scrollRestoration="manual"}),d(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),t.pipe(X("offset"),be(100)).subscribe(({offset:c})=>{history.replaceState(c,"")}),s}var Qn=jt(Kn());function Yn(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,a)=>`${i}${a}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return a=>(0,Qn.default)(a).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function Ht(e){return e.type===1}function fr(e){return e.type===3}function Bn(e,t){let r=ln(e);return T($(location.protocol!=="file:"),We("search")).pipe(He(o=>o),v(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:G("search.suggest")}}})),r}function Gn({document$:e}){let t=we(),r=De(new URL("../versions.json",t.base)).pipe(he(()=>L)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:a,aliases:s})=>a===i||s.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),v(n=>d(document.body,"click").pipe(b(i=>!i.metaKey&&!i.ctrlKey),ne(o),v(([i,a])=>{if(i.target instanceof Element){let s=i.target.closest("a");if(s&&!s.target&&n.has(s.href)){let c=s.href;return!i.target.closest(".md-version")&&n.get(c)===a?L:(i.preventDefault(),$(c))}}return L}),v(i=>{let{version:a}=n.get(i);return mr(new URL(i)).pipe(m(s=>{let p=ve().href.replace(t.base,"");return s.has(p.split("#")[0])?new URL(`../${a}/${p}`,t.base):new URL(i)}))})))).subscribe(n=>st(n,!0)),Q([r,o]).subscribe(([n,i])=>{P(".md-header__topic").appendChild(gn(n,i))}),e.pipe(v(()=>o)).subscribe(n=>{var a;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let s=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(s)||(s=[s]);e:for(let c of s)for(let p of n.aliases.concat(n.version))if(new RegExp(c,"i").test(p)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let s of ie("outdated"))s.hidden=!1})}function Ka(e,{worker$:t}){let{searchParams:r}=ve();r.has("q")&&(Be("search",!0),e.value=r.get("q"),e.focus(),We("search").pipe(He(i=>!i)).subscribe(()=>{let i=ve();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=vt(e),n=T(t.pipe(He(Ht)),d(e,"keyup"),o).pipe(m(()=>e.value),Y());return Q([n,o]).pipe(m(([i,a])=>({value:i,focus:a})),B(1))}function Jn(e,{worker$:t}){let r=new g,o=r.pipe(ee(),oe(!0));Q([t.pipe(He(Ht)),r],(i,a)=>a).pipe(X("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(X("focus")).subscribe(({focus:i})=>{i&&Be("search",i)}),d(e.form,"reset").pipe(U(o)).subscribe(()=>e.focus());let n=P("header [for=__search]");return d(n,"click").subscribe(()=>e.focus()),Ka(e,{worker$:t}).pipe(y(i=>r.next(i)),_(()=>r.complete()),m(i=>F({ref:e},i)),B(1))}function Xn(e,{worker$:t,query$:r}){let o=new g,n=Yo(e.parentElement).pipe(b(Boolean)),i=e.parentElement,a=P(":scope > :first-child",e),s=P(":scope > :last-child",e);We("search").subscribe(l=>s.setAttribute("role",l?"list":"presentation")),o.pipe(ne(r),Ir(t.pipe(He(Ht)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:a.textContent=f.length?ge("search.result.none"):ge("search.result.placeholder");break;case 1:a.textContent=ge("search.result.one");break;default:let u=ar(l.length);a.textContent=ge("search.result.other",u)}});let c=o.pipe(y(()=>s.innerHTML=""),v(({items:l})=>T($(...l.slice(0,10)),$(...l.slice(10)).pipe(Ke(4),jr(n),v(([f])=>f)))),m(hn),le());return c.subscribe(l=>s.appendChild(l)),c.pipe(re(l=>{let f=me("details",l);return typeof f=="undefined"?L:d(f,"toggle").pipe(U(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(b(fr),m(({data:l})=>l)).pipe(y(l=>o.next(l)),_(()=>o.complete()),m(l=>F({ref:e},l)))}function Qa(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=ve();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Zn(e,t){let r=new g,o=r.pipe(ee(),oe(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),d(e,"click").pipe(U(o)).subscribe(n=>n.preventDefault()),Qa(e,t).pipe(y(n=>r.next(n)),_(()=>r.complete()),m(n=>F({ref:e},n)))}function ei(e,{worker$:t,keyboard$:r}){let o=new g,n=Te("search-query"),i=T(d(n,"keydown"),d(n,"focus")).pipe(Oe(ae),m(()=>n.value),Y());return o.pipe(je(i),m(([{suggest:s},c])=>{let p=c.split(/([\s-]+)/);if(s!=null&&s.length&&p[p.length-1]){let l=s[s.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(s=>e.innerHTML=s.join("").replace(/\s/g," ")),r.pipe(b(({mode:s})=>s==="search")).subscribe(s=>{switch(s.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(b(fr),m(({data:s})=>s)).pipe(y(s=>o.next(s)),_(()=>o.complete()),m(()=>({ref:e})))}function ti(e,{index$:t,keyboard$:r}){let o=we();try{let n=Bn(o.search,t),i=Te("search-query",e),a=Te("search-result",e);d(e,"click").pipe(b(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>Be("search",!1)),r.pipe(b(({mode:c})=>c==="search")).subscribe(c=>{let p=Re();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of R(":first-child [href]",a)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,h])=>h-u);f.click()}c.claim()}break;case"Escape":case"Tab":Be("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...R(":not(details) > [href], summary, details[open] [href]",a)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Re()&&i.focus()}}),r.pipe(b(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let s=Jn(i,{worker$:n});return T(s,Xn(a,{worker$:n,query$:s})).pipe($e(...ie("search-share",e).map(c=>Zn(c,{query$:s})),...ie("search-suggest",e).map(c=>ei(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,qe}}function ri(e,{index$:t,location$:r}){return Q([t,r.pipe(q(ve()),b(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>Yn(o.config)(n.searchParams.get("h"))),m(o=>{var a;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let s=i.nextNode();s;s=i.nextNode())if((a=s.parentElement)!=null&&a.offsetHeight){let c=s.textContent,p=o(c);p.length>c.length&&n.set(s,p)}for(let[s,c]of n){let{childNodes:p}=E("span",null,c);s.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Ya(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return Q([r,t]).pipe(m(([{offset:i,height:a},{offset:{y:s}}])=>(a=a+Math.min(n,Math.max(0,s-i))-n,{height:a,locked:s>=i+n})),Y((i,a)=>i.height===a.height&&i.locked===a.locked))}function Qr(e,o){var n=o,{header$:t}=n,r=to(n,["header$"]);let i=P(".md-sidebar__scrollwrap",e),{y:a}=Ue(i);return H(()=>{let s=new g,c=s.pipe(ee(),oe(!0)),p=s.pipe(Me(0,de));return p.pipe(ne(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*a}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(He()).subscribe(()=>{for(let l of R(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=pe(f);f.scrollTo({top:u-h/2})}}}),fe(R("label[tabindex]",e)).pipe(re(l=>d(l,"click").pipe(Oe(ae),m(()=>l),U(c)))).subscribe(l=>{let f=P(`[id="${l.htmlFor}"]`);P(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),Ya(e,r).pipe(y(l=>s.next(l)),_(()=>s.complete()),m(l=>F({ref:e},l)))})}function oi(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return Lt(De(`${r}/releases/latest`).pipe(he(()=>L),m(o=>({version:o.tag_name})),Qe({})),De(r).pipe(he(()=>L),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Qe({}))).pipe(m(([o,n])=>F(F({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return De(r).pipe(m(o=>({repositories:o.public_repos})),Qe({}))}}function ni(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return De(r).pipe(he(()=>L),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Qe({}))}function ii(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return oi(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ni(r,o)}return L}var Ba;function Ga(e){return Ba||(Ba=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(ie("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return L}return ii(e.href).pipe(y(o=>__md_set("__source",o,sessionStorage)))}).pipe(he(()=>L),b(t=>Object.keys(t).length>0),m(t=>({facts:t})),B(1)))}function ai(e){let t=P(":scope > :last-child",e);return H(()=>{let r=new g;return r.subscribe(({facts:o})=>{t.appendChild(bn(o)),t.classList.add("md-source__repository--active")}),Ga(e).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}function Ja(e,{viewport$:t,header$:r}){return Ee(document.body).pipe(v(()=>pr(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),X("hidden"))}function si(e,t){return H(()=>{let r=new g;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(G("navigation.tabs.sticky")?$({hidden:!1}):Ja(e,t)).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}function Xa(e,{viewport$:t,header$:r}){let o=new Map,n=R(".md-nav__link",e);for(let s of n){let c=decodeURIComponent(s.hash.substring(1)),p=me(`[id="${c}"]`);typeof p!="undefined"&&o.set(s,p)}let i=r.pipe(X("height"),m(({height:s})=>{let c=Te("main"),p=P(":scope > :first-child",c);return s+.8*(p.offsetTop-c.offsetTop)}),le());return Ee(document.body).pipe(X("height"),v(s=>H(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let h=f.offsetParent;for(;h;h=h.offsetParent)u+=h.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),je(i),v(([c,p])=>t.pipe(Rr(([l,f],{offset:{y:u},size:h})=>{let w=u+h.height>=Math.floor(s.height);for(;f.length;){let[,A]=f[0];if(A-p=u&&!w)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([s,c])=>({prev:s.map(([p])=>p),next:c.map(([p])=>p)})),q({prev:[],next:[]}),Ke(2,1),m(([s,c])=>s.prev.length{let i=new g,a=i.pipe(ee(),oe(!0));if(i.subscribe(({prev:s,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of s.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===s.length-1)}),G("toc.follow")){let s=T(t.pipe(be(1),m(()=>{})),t.pipe(be(250),m(()=>"smooth")));i.pipe(b(({prev:c})=>c.length>0),je(o.pipe(Oe(ae))),ne(s)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=sr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=pe(f);f.scrollTo({top:u-h/2,behavior:p})}}})}return G("navigation.tracking")&&t.pipe(U(a),X("offset"),be(250),Le(1),U(n.pipe(Le(1))),at({delay:250}),ne(i)).subscribe(([,{prev:s}])=>{let c=ve(),p=s[s.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),Xa(e,{viewport$:t,header$:r}).pipe(y(s=>i.next(s)),_(()=>i.complete()),m(s=>F({ref:e},s)))})}function Za(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:a}})=>a),Ke(2,1),m(([a,s])=>a>s&&s>0),Y()),i=r.pipe(m(({active:a})=>a));return Q([i,n]).pipe(m(([a,s])=>!(a&&s)),Y(),U(o.pipe(Le(1))),oe(!0),at({delay:250}),m(a=>({hidden:a})))}function pi(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new g,a=i.pipe(ee(),oe(!0));return i.subscribe({next({hidden:s}){e.hidden=s,s?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(U(a),X("height")).subscribe(({height:s})=>{e.style.top=`${s+16}px`}),d(e,"click").subscribe(s=>{s.preventDefault(),window.scrollTo({top:0})}),Za(e,{viewport$:t,main$:o,target$:n}).pipe(y(s=>i.next(s)),_(()=>i.complete()),m(s=>F({ref:e},s)))}function li({document$:e}){e.pipe(v(()=>R(".md-ellipsis")),re(t=>yt(t).pipe(U(e.pipe(Le(1))),b(r=>r),m(()=>t),ye(1))),b(t=>t.offsetWidth{let r=t.innerText,o=t.closest("a")||t;return o.title=r,Ge(o).pipe(U(e.pipe(Le(1))),_(()=>o.removeAttribute("title")))})).subscribe(),e.pipe(v(()=>R(".md-status")),re(t=>Ge(t))).subscribe()}function mi({document$:e,tablet$:t}){e.pipe(v(()=>R(".md-toggle--indeterminate")),y(r=>{r.indeterminate=!0,r.checked=!1}),re(r=>d(r,"change").pipe(Fr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),ne(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function es(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function fi({document$:e}){e.pipe(v(()=>R("[data-md-scrollfix]")),y(t=>t.removeAttribute("data-md-scrollfix")),b(es),re(t=>d(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function ui({viewport$:e,tablet$:t}){Q([We("search"),t]).pipe(m(([r,o])=>r&&!o),v(r=>$(r).pipe(Ye(r?400:100))),ne(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ts(){return location.protocol==="file:"?gt(`${new URL("search/search_index.js",Yr.base)}`).pipe(m(()=>__index),B(1)):De(new URL("search/search_index.json",Yr.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var rt=No(),Rt=Jo(),wt=en(Rt),Br=Go(),_e=pn(),ur=At("(min-width: 960px)"),hi=At("(min-width: 1220px)"),bi=tn(),Yr=we(),vi=document.forms.namedItem("search")?ts():qe,Gr=new g;Wn({alert$:Gr});var Jr=new g;G("navigation.instant")&&zn({location$:Rt,viewport$:_e,progress$:Jr}).subscribe(rt);var di;((di=Yr.version)==null?void 0:di.provider)==="mike"&&Gn({document$:rt});T(Rt,wt).pipe(Ye(125)).subscribe(()=>{Be("drawer",!1),Be("search",!1)});Br.pipe(b(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=me("link[rel=prev]");typeof t!="undefined"&&st(t);break;case"n":case".":let r=me("link[rel=next]");typeof r!="undefined"&&st(r);break;case"Enter":let o=Re();o instanceof HTMLLabelElement&&o.click()}});li({document$:rt});mi({document$:rt,tablet$:ur});fi({document$:rt});ui({viewport$:_e,tablet$:ur});var tt=Rn(Te("header"),{viewport$:_e}),$t=rt.pipe(m(()=>Te("main")),v(e=>Fn(e,{viewport$:_e,header$:tt})),B(1)),rs=T(...ie("consent").map(e=>fn(e,{target$:wt})),...ie("dialog").map(e=>$n(e,{alert$:Gr})),...ie("header").map(e=>Pn(e,{viewport$:_e,header$:tt,main$:$t})),...ie("palette").map(e=>jn(e)),...ie("progress").map(e=>Un(e,{progress$:Jr})),...ie("search").map(e=>ti(e,{index$:vi,keyboard$:Br})),...ie("source").map(e=>ai(e))),os=H(()=>T(...ie("announce").map(e=>mn(e)),...ie("content").map(e=>Hn(e,{viewport$:_e,target$:wt,print$:bi})),...ie("content").map(e=>G("search.highlight")?ri(e,{index$:vi,location$:Rt}):L),...ie("header-title").map(e=>In(e,{viewport$:_e,header$:tt})),...ie("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?Ur(hi,()=>Qr(e,{viewport$:_e,header$:tt,main$:$t})):Ur(ur,()=>Qr(e,{viewport$:_e,header$:tt,main$:$t}))),...ie("tabs").map(e=>si(e,{viewport$:_e,header$:tt})),...ie("toc").map(e=>ci(e,{viewport$:_e,header$:tt,main$:$t,target$:wt})),...ie("top").map(e=>pi(e,{viewport$:_e,header$:tt,main$:$t,target$:wt})))),gi=rt.pipe(v(()=>os),$e(rs),B(1));gi.subscribe();window.document$=rt;window.location$=Rt;window.target$=wt;window.keyboard$=Br;window.viewport$=_e;window.tablet$=ur;window.screen$=hi;window.print$=bi;window.alert$=Gr;window.progress$=Jr;window.component$=gi;})(); +//# sourceMappingURL=bundle.1e8ae164.min.js.map + diff --git a/assets/javascripts/bundle.1e8ae164.min.js.map b/assets/javascripts/bundle.1e8ae164.min.js.map new file mode 100644 index 000000000..6c33b8e8e --- /dev/null +++ b/assets/javascripts/bundle.1e8ae164.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/clipboard/dist/clipboard.js", "node_modules/escape-html/index.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/rxjs/node_modules/tslib/tslib.es6.js", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*\n * Copyright (c) 2016-2024 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an