1"use strict";(self.webpackChunkrabbitmq_website=self.webpackChunkrabbitmq_website||[]).push([["17300"],{41326(e,n,r){r.r(n),r.d(n,{metadata:()=>i,default:()=>m,frontMatter:()=>l,contentTitle:()=>d,toc:()=>h,assets:()=>c});var i=JSON.parse('{"id":"ha/index","title":"Classic Queue Mirroring","description":"\x3c!--","source":"@site/versioned_docs/version-3.13/ha/index.md","sourceDirName":"ha","slug":"/ha/","permalink":"/docs/3.13/ha/","draft":false,"unlisted":false,"editUrl":"https://github.com/rabbitmq/rabbitmq-website/tree/main/versioned_docs/version-3.13/ha/index.md","tags":[],"version":"3.13","frontMatter":{"title":"Classic Queue Mirroring"},"sidebar":"docsSidebar","previous":{"title":"Classic Queues","permalink":"/docs/3.13/classic-queues"},"next":{"title":"Migrate Mirrored Classic Queues to Quorum Queues","permalink":"/docs/3.13/migrate-mcq-to-qq"}}'),s=r(74848),a=r(28453),t=r(50773),o=r(57250);let l={title:"Classic Queue Mirroring"},d="Classic Queue Mirroring (Deprecated)",c={},h=[{value:"Wait, There's a Better Way: Modern Replicated Queue Type and Streams",id:"interstitial",level:2},{value:"Overview",id:"overview",level:2},{value:"What is Queue Mirroring",id:"what-is-mirroring",level:2},{value:"How Mirroring is Configured",id:"ways-to-configure",level:2},{value:"Queue Arguments that Control Mirroring",id:"mirroring-arguments",level:2},{value:"Replication Factor: How Many Mirrors are Optimal?",id:"replication-factor",level:3},{value:"How to Check if a Queue is Mirrored?",id:"how-to-check-i-a-queue-is-mirrored",level:2},{value:"How to Detect Policies that Enable Classic Queue Mirroring",id:"detect-usage",level:2},{value:"Mirroring and CQv2",id:"cqv2",level:2},{value:"Queue Leader Replicas, Leader Migration, Data Locality",id:"leader-migration-data-locality",level:2},{value:"Queue Leader Location",id:"queue-leader-location",level:3},{value:""nodes" Policy and Migrating Leaders",id:"fixed-leader-promotion",level:3},{value:"Mirroring of Exclusive Queues",id:"exclusive-queues-are-not-mirrored",level:3},{value:"Non-mirrored Queue Behavior in a Cluster",id:"non-mirrored-queue-behavior-on-node-failure",level:2},{value:"Examples",id:"examples",level:2},{value:"Mirrored Queue Implementation and Semantics",id:"behaviour",level:2},{value:"Publisher Confirms and Transactions",id:"confirms-transactions",level:3},{value:"Flow Control",id:"flow-control",level:3},{value:"Leader Failures and Consumer Cancellation",id:"cancellation",level:3},{value:"Unsynchronised Mirrors",id:"unsynchronised-mirrors",level:2},{value:"Promotion of Unsynchronised Mirrors on Failure",id:"promoting-unsynchronised-mirrors",level:3},{value:"Stopping Nodes and Synchronisation",id:"start-stop",level:3},{value:"Stopping Nodes Hosting Queue Leader with Only Unsynchronised Mirrors",id:"cluster-shutdown",level:3},{value:"Loss of a Leader While All Mirrors are Stopped",id:"promotion-while-down",level:3},{value:"Batch Synchronization",id:"batch-sync",level:2},{value:"Configuring Synchronisation",id:"configuring-synchronisation",level:3}];function u(e){let n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",header:"header",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"classic-queue-mirroring-deprecated",children:"Classic Queue Mirroring (Deprecated)"})}),"\n",(0,s.jsx)(n.admonition,{type:"danger",children:(0,s.jsxs)(n.p,{children:["This guide covers a feature that had been ",(0,s.jsx)(n.a,{href:"/blog/2021/08/21/4.0-deprecation-announcements",children:(0,s.jsx)(n.strong,{children:"deprecated since 2021"})})," and ",(0,s.jsx)(n.a,{href:"https://github.com/rabbitmq/rabbitmq-server/pull/9815",children:(0,s.jsx)(n.strong,{children:"was removed completely"})})," starting with RabbitMQ 4.0."]})}),"\n",(0,s.jsx)(n.admonition,{type:"important",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"./quorum-queues",children:"Quorum queues"})," and/or ",(0,s.jsx)(n.a,{href:"./streams",children:"streams"})," should be used instead of mirrored classic queues.\n",(0,s.jsx)(n.strong,{children:"Non-replicated"})," classic queues continue being supported and developed."]})}),"\n",(0,s.jsx)(n.admonition,{type:"tip",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"rabbitmqadmin"})," v2 provides ",(0,s.jsx)(n.a,{href:"https://www.rabbitmq.com/blog/2025/07/29/latest-benefits-of-rmq-and-migrating-to-qq-along-the-way",children:"modern tooling for migrating away from Classic Mirrored Queues to Quorum Queues"})]})}),"\n",(0,s.jsx)(n.h2,{id:"interstitial",children:"Wait, There's a Better Way: Modern Replicated Queue Type and Streams"}),"\n",(0,s.jsxs)(n.p,{children:["This guide covers a ",(0,s.jsx)(n.a,{href:"/blog/2021/08/21/4.0-deprecation-announcements",children:(0,s.jsx)(n.strong,{children:"long time deprecated"})})," and ",(0,s.jsx)(n.a,{href:"https://github.com/rabbitmq/rabbitmq-server/pull/9815",children:(0,s.jsx)(n.strong,{children:"in 4.x, removed"})})," legacy feature: mirroring (queue contents replication) of classic queues.\n",(0,s.jsx)(n.a,{href:"./quorum-queues",children:"Quorum queues"}
1)," and/or ",(0,s.jsx)(n.a,{href:"./streams",children:"streams"})," should be used instead of mirrored classic queues."]}),"\n",(0,s.jsxs)(n.p,{children:["Quorum queues are a more advanced queue type, which offers high availability using replication and focuses on data safety. Quorum queues ",(0,s.jsx)(n.a,{href:"/blog/2022/05/05/rabbitmq-3.10-release-overview",children:"support message TTL"})," and provide ",(0,s.jsx)(n.a,{href:"/blog/2022/05/16/rabbitmq-3.10-performance-improvements",children:"higher throughput and more stable latency"})," compared to mirrored classic queues. Please migrate from Mirrored Classic Queues to Quorum Queues, either ",(0,s.jsx)(n.a,{href:"/blog/2025/07/29/latest-benefits-of-rmq-and-migrating-to-qq-along-the-way",children:"using the Blue-Green Deployment strategy"})," or ",(0,s.jsx)(n.a,{href:"./migrate-mcq-to-qq",children:"within the same virtual host"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"./streams",children:"Streams"})," is an ",(0,s.jsx)(n.a,{href:"/blog/2021/07/13/rabbitmq-streams-overview",children:"alternative messaging data structure"})," supported by RabbitMQ.\nJust like quorum queues, streams are replicated."]}),"\n",(0,s.jsxs)(n.p,{children:["Quorum queues should be the ",(0,s.jsx)(n.strong,{children:"default choice"})," for a replicated queue type.\nClassic queue mirroring will be ",(0,s.jsx)(n.strong,{children:"removed in a future version"})," of RabbitMQ:\nclassic queues will remain a supported non-replicated queue type."]}),"\n",(0,s.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,s.jsx)(n.p,{children:"Topics covered in this guide include,\nfor migrating away from classic mirrored queues:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#interstitial",children:"Next generation replicated queues and streams"}),", and why they should be preferred over classic queue mirroring"]}),"\n",(0,s.jsxs)(n.li,{children:["How to ",(0,s.jsx)(n.a,{href:"#detect-usage",children:"detect what policies enable classic queue mirroring"})]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"For historical reference:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["What is ",(0,s.jsx)(n.a,{href:"#what-is-mirroring",children:"classic queue mirroring"})," and how it works"]}),"\n",(0,s.jsxs)(n.li,{children:["How to ",(0,s.jsx)(n.a,{href:"#ways-to-configure",children:"enable it"})]}),"\n",(0,s.jsxs)(n.li,{children:["What ",(0,s.jsx)(n.a,{href:"#mirroring-arguments",children:"mirroring settings are available"})]}),"\n",(0,s.jsxs)(n.li,{children:["Why ",(0,s.jsx)(n.a,{href:"#cqv2",children:"mixed CQv1 and CQv2 clusters are not recommended with mirroring"})]}),"\n",(0,s.jsxs)(n.li,{children:["What ",(0,s.jsx)(n.a,{href:"#replication-factor",children:"replication factor"})," is recommended"]}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#leader-migration-data-locality",children:"Data locality"})}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#behaviour",children:"Leader election"})," (mirror promotion) and ",(0,s.jsx)(n.a,{href:"#unsynchronised-mirrors",children:"unsynchronised mirrors"})]}),"\n",(0,s.jsxs)(n.li,{children:["Mirrored vs. ",(0,s.jsx)(n.a,{href:"#non-mirrored-queue-behavior-on-node-failure",children:"non-mirrored queue behavior"})," in case of node failure"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#batch-sync",children:"Batch synchronisation"})," of newly added and recovering mirrors"]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"and more."}),"\n",(0,s.jsxs)(n.p,{children:["This guide assumes general familiarity with ",(0,s.jsx)(n.a,{href:"./clustering",children:"RabbitMQ clustering"}),", ",(0,s.jsx)(n.a,{href:"./quorum-queues",children:"quorum queues"}),", and ",(0,s.jsx)(n.a,{href:"./streams",children:"streams"}),"."]}),"\n",(0,s.jsx)(n.h2,{id:"what-is-mirroring",children:"What is Queue Mirroring"}),"\n",(0,s.jsx)(n.admonition,{type:"danger",children:(0,s.jsxs)(n.p,{children:["This guide covers a feature that had been ",(0,s.jsx)(n.a,{href:"/blog/2021/08/21/4.0-deprecation-announcements",children:(0,s.jsx)(n.strong,{children:"deprecated since 2021"})}),"\nand ",(0,s.jsx)(n.a,{href:"https://github.com/rabbitmq/rabbitmq-server/pull/9815",children:(0,s.jsx)(n.strong,{children:"was removed completely"})})," starting with RabbitMQ 4.0."]})}),"\n",(0,s.jsx)(n.admonition,{type:"important",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"./quorum-queues",children:"Quorum queues"}
1)," and/or ",(0,s.jsx)(n.a,{href:"./streams",children:"streams"})," should be used instead of mirrored classic queues.\n",(0,s.jsx)(n.strong,{children:"Non-replicated"})," classic queues continue being supported and developed."]})}),"\n",(0,s.jsx)(n.p,{children:"By default, contents of a queue within a RabbitMQ cluster are located on\na single node (the node on which the queue was\ndeclared). This is in contrast to exchanges and bindings,\nwhich can always be considered to be on all nodes. Queues\ncan optionally run mirrors (additional replicas) on other cluster nodes."}),"\n",(0,s.jsxs)(n.p,{children:["Each mirrored queue consists of one ",(0,s.jsx)(n.strong,{children:"leader replica"})," and\none or more ",(0,s.jsx)(n.strong,{children:"mirrors"})," (replicas). The leader is hosted on one\nnode commonly referred as the leader node for that queue. Each queue has\nits own leader node. All operations for a given queue are first applied\non the queue's leader node and then propagated to mirrors. This\ninvolves enqueueing publishes, delivering messages to consumers, tracking\n",(0,s.jsx)(n.a,{href:"./confirms",children:"acknowledgements from consumers"})," and so on."]}),"\n",(0,s.jsxs)(n.p,{children:["Queue mirroring implies ",(0,s.jsx)(n.a,{href:"./clustering",children:"a cluster of nodes"}),".\nIt is therefore not recommended for use\nacross a WAN (though of course, clients can still connect\nfrom as near and as far as needed)."]}),"\n",(0,s.jsx)(n.p,{children:"Messages published to the queue are replicated to all\nmirrors. Consumers are connected to the leader regardless of\nwhich node they connect to, with mirrors dropping messages\nthat have been acknowledged at the leader. Queue mirroring\ntherefore enhances availability, but does not distribute\nload across nodes (all participating nodes each do all the\nwork)."}),"\n",(0,s.jsxs)(n.p,{children:["If the node that hosts queue leader fails, the oldest mirror will be\npromoted to the new leader as long as it's synchronised. ",(0,s.jsx)(n.a,{href:"#unsynchronised-mirrors",children:"Unsynchronised mirrors"}),"\ncan be promoted, too, depending on queue mirroring parameters."]}),"\n",(0,s.jsx)(n.p,{children:'There are multiple terms commonly used to identify primary\nand secondary replicas in a distributed system. This guide\ntypically uses "leader" to refer to the primary replica of a\nqueue and "mirror" for secondary replicas.'}),"\n",(0,s.jsx)(n.p,{children:'Queue object fields in the HTTP API and CLI tools originally used the unfortunate term\n"slave" to refer to secondaries. That term still appears\nin column names in CLI tools for backwards compatibility but will be\nreplaced or removed in a future version.'}),"\n",(0,s.jsx)(n.h2,{id:"ways-to-configure",children:"How Mirroring is Configured"}),"\n",(0,s.jsx)(n.admonition,{type:"danger",children:(0,s.jsxs)(n.p,{children:["This guide covers a feature that had been ",(0,s.jsx)(n.a,{href:"/blog/2021/08/21/4.0-deprecation-announcements",children:(0,s.jsx)(n.strong,{children:"deprecated since 2021"})}),"\nand ",(0,s.jsx)(n.a,{href:"https://github.com/rabbitmq/rabbitmq-server/pull/9815",children:(0,s.jsx)(n.strong,{children:"was removed completely"})})," starting with RabbitMQ 4.0."]})}),"\n",(0,s.jsx)(n.admonition,{type:"important",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"./quorum-queues",children:"Quorum queues"})," and/or ",(0,s.jsx)(n.a,{href:"./streams",children:"streams"})," should be used instead of mirrored classic queues.\n",(0,s.jsx)(n.strong,{children:"Non-replicated"})," classic queues continue being supported and developed."]})}),"\n",(0,s.jsxs)(n.p,{children:["Mirroring parameters are configured using ",(0,s.jsx)(n.a,{href:"./policies",children:"policies"}),". A policy matches\none or more queues by name (using a regular expression pattern) and\ncontains a definition (a map of optional arguments) that are added to the total set of\nproperties of the matching queues."]}),"\n",(0,s.jsxs)(n.p,{children:["Please see ",(0,s.jsx)(n.a,{href:"./policies",children:"Runtime Parameters and Policies"})," for more information on policies."]}),"\n",(0,s.jsx)(n.h2,{id:"mirroring-arguments",children:"Queue Arguments that Control Mirroring"}),"\n",(0,s.jsx)(n.admonition,{type:"danger",children:(0,s.jsxs)(n.p,{children:["This guide covers a feature that had been ",(0,s.jsx)(n.a,{href:"/blog/2021/08/21/4.0-deprecation-announcements",children:(0,s.jsx)(n.strong,{children:"deprecated since 2021"})}),"\nand ",(0,s.jsx)(n.a,{href:"https://github.com/rabbitmq/rabbitmq-server/pull/9815",children:(0,s.jsx)(n.strong,{children:"was removed completely"})})," starting with RabbitMQ 4.0."]})}),"\n",(0,s.jsx)(n.admonition,{type:"important",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"./quorum-queues",children:"Quorum queues"}
1)," and/or ",(0,s.jsx)(n.a,{href:"./streams",children:"streams"})," should be used instead of mirrored classic queues.\n",(0,s.jsx)(n.strong,{children:"Non-replicated"})," classic queues continue being supported and developed."]})}),"\n",(0,s.jsxs)(n.p,{children:["As we've covered above, queues have mirroring enabled\nvia ",(0,s.jsx)(n.a,{href:"./policies",children:"policy"}),". Policies\ncan change at any time; it is valid to create a non-mirrored\nqueue, and then make it mirrored at some later point (and\nvice versa). There is a difference between a non-mirrored\nqueue and a mirrored queue which does not have any mirrors -\nthe former lacks the extra mirroring infrastructure and will\nlikely provide higher throughput."]}),"\n",(0,s.jsxs)(n.p,{children:["Adding mirrors to a queue increases cluster load but\nhelps lower the probability of ",(0,s.jsx)(n.a,{href:"#unsynchronised-mirrors",children:"losing all up-to-date replicas"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["To make the classic queues mirrored, create a\npolicy which matches them and sets policy keys ",(0,s.jsx)(n.code,{children:"ha-mode"})," and (optionally) ",(0,s.jsx)(n.code,{children:"ha-params"}),".\nThe following table explains the options for these keys:"]}),"\n",(0,s.jsxs)("table",{children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{children:[(0,s.jsx)("th",{children:(0,s.jsx)("code",{children:"ha-mode"})}),(0,s.jsx)("th",{children:(0,s.jsx)("code",{children:"ha-params"})}),(0,s.jsx)("th",{children:"Result"})]})}),(0,s.jsxs)("tbody",{children:[(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"exactly"})}),(0,s.jsx)("td",{children:(0,s.jsx)("i",{children:"count"})}),(0,s.jsxs)("td",{children:[(0,s.jsx)(n.p,{children:"Number of queue replicas (leader plus mirrors) in the cluster."}),(0,s.jsxs)(n.p,{children:["A ",(0,s.jsx)("i",{children:"count"})," value of 1 means a single replica: just the queue leader.\nIf the node running the queue leader becomes\nunavailable, ",(0,s.jsx)("a",{href:"#non-mirrored-queue-behavior-on-node-failure",children:"the behaviour depends on queue durability"}),"."]}),(0,s.jsxs)(n.p,{children:["A ",(0,s.jsx)("i",{children:"count"})," value of 2 means 2 replicas: 1 queue leader and 1 queue\nmirror. In other words: ",(0,s.jsx)(n.code,{children:"NumberOfQueueMirrors = NumberOfNodes - 1"}),".\nIf the node running the queue leader becomes unavailable,\nthe queue mirror will be automatically promoted to leader\naccording to the ",(0,s.jsx)("a",{href:"#unsynchronised-mirrors",children:"mirror promotion strategy"})," configured."]}),(0,s.jsxs)(n.p,{children:["If there are fewer than ",(0,s.jsx)("i",{children:"count"})," nodes in the cluster, the\nqueue is mirrored to all nodes. If there are more than\n",(0,s.jsx)("i",{children:"count"})," nodes in the cluster, and a node containing a mirror\ngoes down, then a new mirror will be created on another node. Use\nof ",(0,s.jsx)(n.code,{children:"exactly"})," mode with ",(0,s.jsxs)("a",{href:"#cluster-shutdown",children:["\n",(0,s.jsx)(n.code,{children:'"ha-promote-on-shutdown": "always"'})]})," can be\ndangerous since queues can migrate across a cluster and become\nunsynced as it is brought down."]})]})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"all"})}),(0,s.jsx)("td",{children:"(none)"}),(0,s.jsxs)("td",{children:[(0,s.jsx)(n.p,{children:"Queue is mirrored across all nodes in the\ncluster. When a new node is added to the cluster, the\nqueue will be mirrored to that node."}),(0,s.jsxs)(n.p,{children:["This setting is very\nconservative. Mirroring to a quorum (N/2 + 1) of cluster nodes\nis ",(0,s.jsx)("a",{href:"#replication-factor",children:"recommended instead"}),".\nMirroring to all nodes will put additional\nstrain on all cluster nodes, including network I/O, disk I/O and\ndisk space usage."]})]})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"nodes"})}),(0,s.jsx)("td",{children:(0,s.jsx)("i",{children:"node names"})}),(0,s.jsxs)("td",{children:[(0,s.jsxs)(n.p,{children:["Queue is mirrored to the nodes listed in ",(0,s.jsx)("i",{children:"node names"}),".\nNode names are the Erlang node names as they\nappear in ",(0,s.jsx)("code",{children:"rabbitmqctl cluster_status"}),'; they\nusually have the form "',(0,s.jsx)("tt",{children:"rabbit@hostname"}),'".']}),(0,s.jsx)(n.p,{children:"If any of those node names are not a part of the cluster,\nthis does not constitute an error. If none of the nodes\nin the list are online at the time when the queue is\ndeclared then the queue will be created on the node that\nthe declaring client is connected to."})]})]})]})]}),"\n",(0,s.jsx)(n.p,{children:"Whenever the HA policy for a queue changes it will endeavour\nto keep its existing mirrors as far as this fits with the new\npolicy."}),"\n",(0,s.jsx)(n.h3,{id:"replication-factor",children:"Replication Factor: How Many Mirrors are Optimal?"}),"\n",(0,s.jsx)(n.p,{children:"Mirroring to all nodes is the most conservative option.\nIt will put additional strain on all cluster nodes, including network I/O, disk I/O and\ndisk space usage. Having a replica on every node is unnecessary in most cases."}
1),"\n",(0,s.jsx)(n.p,{children:"For clusters of 3 and more nodes\nit is recommended to replicate to a quorum (the majority) of nodes,\ne.g. 2 nodes in a 3 node cluster or 3 nodes in a 5 node cluster."}),"\n",(0,s.jsx)(n.p,{children:"Since some data can be inherently transient or very time sensitive,\nit can be perfectly reasonable to use a lower number of mirrors\nfor some queues (or even not use any mirroring)."}),"\n",(0,s.jsx)(n.h2,{id:"how-to-check-i-a-queue-is-mirrored",children:"How to Check if a Queue is Mirrored?"}),"\n",(0,s.jsxs)(n.p,{children:["Mirrored queues will have a policy name and the number of additional replicas (mirrors)\nnext to it on the queue page in the ",(0,s.jsx)(n.a,{href:"./management",children:"management UI"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["Below is an example of a queue named ",(0,s.jsx)(n.code,{children:"two.replicas"})," which has a leader\nand a mirror:"]}),"\n",(0,s.jsx)("figure",{children:(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"Mirrored queue indicators in management UI",src:r(22326).A+"",width:"944",height:"200"})})}),"\n",(0,s.jsx)(n.p,{children:"leader node for the queue and its online mirror(s), if any, will be listed on the queue page:"}),"\n",(0,s.jsx)("figure",{children:(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"Mirrored queue details on individual queue page",src:r(83069).A+"",width:"710",height:"492"})})}),"\n",(0,s.jsx)(n.p,{children:"If the queue page does not list any mirrors, the queue is not mirrored (or has only one mirror which\nis not online):"}),"\n",(0,s.jsx)("figure",{children:(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"Non-mirrored queue details on individual queue page",src:r(8627).A+"",width:"701",height:"498"})})}),"\n",(0,s.jsx)(n.p,{children:"When a new queue mirror is added, the event is logged:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:"2018-03-01 07:26:33.121 [info] <0.1360.0> Mirrored queue 'two.replicas' in vhost '/': Adding mirror on node hare@warp10: <37324.1148.0>\n"})}),"\n",(0,s.jsxs)(n.p,{children:["It is possible to list queue leader and mirrors using ",(0,s.jsx)(n.code,{children:"rabbitmqctl list_queues"}),". In this\nexample we also display queue policy since it's highly relevant:"]}),"\n",(0,s.jsxs)(t.A,{groupId:"examples",children:[(0,s.jsx)(o.A,{value:"bash",label:"bash",default:!0,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# mirror_pids is a new field alias introduced in RabbitMQ 3.11.4\nrabbitmqctl list_queues name policy pid mirror_pids\n\n# => Timeout: 60.0 seconds ...\n# => Listing queues for vhost / ...\n# => two.replicas ha-two <[email protected]> [<[email protected]>]\n"})})}),(0,s.jsx)(o.A,{value:"PowerShell",label:"PowerShell",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-PowerShell",children:"rabbitmqctl.bat list_queues name policy pid mirror_pids\n\n# => Timeout: 60.0 seconds ...\n# => Listing queues for vhost / ...\n# => two.replicas ha-two <[email protected]> [<[email protected]>]\n"})})}),(0,s.jsx)(o.A,{value:"cmd",label:"cmd",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-batch",children:"rem mirror_pids is a new field alias introduced in RabbitMQ 3.11.4\nrabbitmqctl.bat list_queues name policy pid mirror_pids\n\nrem => Timeout: 60.0 seconds ...\nrem => Listing queues for vhost / ...\nrem => two.replicas ha-two <[email protected]> [<[email protected]>]\n"})})})]}),"\n",(0,s.jsxs)(n.p,{children:["If a queue that's expected to be mirroring is not, this usually means that its name\ndoesn't match that specified in the policy that controls mirroring or that another\npolicy takes priority (and does not enable mirroring).\nSee ",(0,s.jsx)(n.a,{href:"./policies",children:"Runtime Parameters and Policies"})," to learn more."]}),"\n",(0,s.jsx)(n.h2,{id:"detect-usage",children:"How to Detect Policies that Enable Classic Queue Mirroring"}),"\n",(0,s.jsx)(n.p,{children:"In order to prepare for migration away from classic mirrored queues, it may be necessary to first\nunderstand whether there are any policies in the cluster that enable use this deprecated feature."}),"\n",(0,s.jsxs)(n.p,{children:["There are two ",(0,s.jsx)(n.code,{children:"rabbitmq-diagnostics"})," commands that help with this:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"rabbitmq-diagnostics check_if_cluster_has_classic_queue_mirroring_policy"}),", a ",(0,s.jsx)(n.a,{href:"./monitoring#health-checks",children:"health check"})]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"rabbitmq-diagnostics list_policies_with_classic_queue_mirroring"})," that lists the problematic policies"]}
1),"\n"]}),"\n",(0,s.jsxs)(t.A,{groupId:"examples",children:[(0,s.jsx)(o.A,{value:"bash",label:"bash",default:!0,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# exits with a non-zero code if any policies in the cluster\n# enable classic queue mirroring\nrabbitmq-diagnostics check_if_cluster_has_classic_queue_mirroring_policy\n\n# lists policies that enable classic queue mirroring\nrabbitmq-diagnostics list_policies_with_classic_queue_mirroring -s --formatter=pretty_table\n"})})}),(0,s.jsx)(o.A,{value:"PowerShell",label:"PowerShell",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-PowerShell",children:"# exits with a non-zero code if any policies in the cluster\n# enabling classic queue mirroring\nrabbitmq-diagnostics.bat check_if_cluster_has_classic_queue_mirroring_policy\n\n# lists policies that enable classic queue mirroring\nrabbitmq-diagnostics.bat list_policies_with_classic_queue_mirroring -s --formatter=pretty_table\n"})})}),(0,s.jsx)(o.A,{value:"cmd",label:"cmd",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-batch",children:"rem exits with a non-zero code if any policies in the cluster\nrem enabling classic queue mirroring\nrabbitmq-diagnostics.bat check_if_cluster_has_classic_queue_mirroring_policy\n\nrem lists policies that enable classic queue mirroring\nrabbitmq-diagnostics.bat list_policies_with_classic_queue_mirroring -s --formatter=pretty_table\n"})})})]}),"\n",(0,s.jsx)(n.h2,{id:"cqv2",children:"Mirroring and CQv2"}),"\n",(0,s.jsx)(n.admonition,{type:"warning",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"./persistence-conf#queue-version",children:"Classic queues version 2"})," can be used with mirroring.\nHowever, combining v1 and v2 members is not recommended"]})}),"\n",(0,s.jsx)(n.p,{children:"It may happen if some nodes default to version 1 while other\nnodes default to version 2 (a new mirror will use the local node's default version if not explicitly set). Version 2\nis significantly faster in many situations and can overload a v1 mirror. The easiest solution is to switch to version 2\nusing policies before changing the default version in the configuration."}),"\n",(0,s.jsx)(n.h2,{id:"leader-migration-data-locality",children:"Queue Leader Replicas, Leader Migration, Data Locality"}),"\n",(0,s.jsx)(n.h3,{id:"queue-leader-location",children:"Queue Leader Location"}),"\n",(0,s.jsxs)(n.p,{children:["This section has been moved to the ",(0,s.jsx)(n.a,{href:"./clustering#replica-placement",children:"Clustering guide"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"fixed-leader-promotion",children:'"nodes" Policy and Migrating Leaders'}),"\n",(0,s.jsx)(n.p,{children:'Note that setting or modifying a "nodes" policy can cause\nthe existing leader to go away if it is not listed in the\nnew policy. In order to prevent message loss, RabbitMQ will\nkeep the existing leader around until at least one other\nmirror has synchronised (even if this is a long\ntime). However, once synchronisation has occurred things will\nproceed just as if the node had failed: consumers will be\ndisconnected from the leader and will need to reconnect.'}),"\n",(0,s.jsxs)(n.p,{children:["For example, if a queue is on ",(0,s.jsx)(n.code,{children:"[A B]"}),"\n(with ",(0,s.jsx)(n.code,{children:"A"})," the leader), and you give it\na ",(0,s.jsx)(n.code,{children:"nodes"})," policy telling it to be on\n",(0,s.jsx)(n.code,{children:"[C D]"}),", it will initially end up on\n",(0,s.jsx)(n.code,{children:"[A C D]"}),". As soon as the queue synchronises on its new\nmirrors ",(0,s.jsx)(n.code,{children:"[C D]"}),", the leader on ",(0,s.jsx)(n.code,{children:"A"}),"\nwill shut down."]}),"\n",(0,s.jsx)(n.h3,{id:"exclusive-queues-are-not-mirrored",children:"Mirroring of Exclusive Queues"}),"\n",(0,s.jsx)(n.p,{children:"Exclusive queues will be deleted when the connection that declared them is\nclosed. For this reason, it is not useful for an exclusive queue to be mirrored\n(or a non-durable queue, for that matter) since when the node hosting it goes\ndown, the connection will close and the queue will need to be deleted anyway."}),"\n",(0,s.jsx)(n.p,{children:"For this reason, exclusive queues are never mirrored (even if they match a\npolicy stating that they should be). They are also never durable (even if\ndeclared as such)."}),"\n",(0,s.jsx)(n.h2,{id:"non-mirrored-queue-behavior-on-node-failure",children:"Non-mirrored Queue Behavior in a Cluster"}),"\n",(0,s.jsx)(n.p,{children:"This guide focuses on mirrored queues, however, it is important\nto briefly explain how non-mirrored queues behave in a cluster in contrast\nwith mirrored ones."}),"\n",(0,s.jsx)(n.p,{children:"If leader node of a queue (the node running queue leader) is available,\nall queue operations (e.g. declaration, binding and consumer management, message routing\nto the queue) can be performed on any no
1de. Cluster nodes will route\noperations to the leader node transparently to the clients."}),"\n",(0,s.jsx)(n.p,{children:"If leader node of a queue\nbecomes unavailable, the behaviour of a non-mirrored queue\ndepends on its durability. A durable queue will become\nunavailable until the node comes back.\nAll operations on a durable queue with unavailable leader node\nwill fail with a message in server logs that looks like this:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"operation queue.declare caused a channel exception not_found: home node 'rabbit@hostname' of durable queue 'queue-name' in vhost '/' is down or inaccessible\n"})}),"\n",(0,s.jsx)(n.p,{children:"A non-durable one will be deleted."}),"\n",(0,s.jsxs)(n.p,{children:["In case it is desired that the queue remains available at all times,\nmirrors can be configured to be ",(0,s.jsx)(n.a,{href:"#unsynchronised-mirrors",children:"promoted to leader even when not in sync"}),"."]}),"\n",(0,s.jsx)(n.h2,{id:"examples",children:"Examples"}),"\n",(0,s.jsxs)(n.p,{children:['Below is a policy where queues whose names begin with\n"',(0,s.jsx)(n.code,{children:"two."}),'" are mirrored to any two nodes in the\ncluster, with ',(0,s.jsx)(n.a,{href:"#configuring-synchronisation",children:"automatic synchronisation"}),":"]}),"\n",(0,s.jsxs)(t.A,{groupId:"examples",children:[(0,s.jsx)(o.A,{value:"bash",label:"bash",default:!0,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'rabbitmqctl set_policy ha-two "^two\\." \\\n \'{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}\'\n'})})}),(0,s.jsx)(o.A,{value:"PowerShell",label:"PowerShell",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-PowerShell",children:'rabbitmqctl.bat set_policy ha-two "^two\\." ^\n "{""ha-mode"":""exactly"",""ha-params"":2,""ha-sync-mode"":""automatic""}"\n'})})}),(0,s.jsx)(o.A,{value:"http-api",label:"HTTP API",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:'PUT /api/policies/%2f/ha-two\n{\n "pattern":"^two\\.",\n "definition": {\n "ha-mode":"exactly",\n "ha-params":2,\n "ha-sync-mode":"automatic"\n }\n}\n'})})}),(0,s.jsx)(o.A,{value:"ui",label:"Management UI",children:(0,s.jsxs)("ul",{children:[(0,s.jsx)("li",{children:(0,s.jsxs)(n.p,{children:["Navigate to ",(0,s.jsx)(n.code,{children:"Admin"})," > ",(0,s.jsx)(n.code,{children:"Policies"})," > ",(0,s.jsx)(n.code,{children:"Add / update a policy"}),"."]})}),(0,s.jsx)("li",{children:(0,s.jsxs)(n.p,{children:['Enter "ha-two" next to Name and "^two." next to\n',(0,s.jsx)(n.code,{children:"Pattern"}),"."]})}),(0,s.jsx)("li",{children:(0,s.jsx)(n.p,{children:'Enter "ha-mode" = "exactly" in the first line\nnext to Policy, then "ha-params" = 2 in the second\nline, then "ha-sync-mode" = "automatic" in the third,\nand set the type on the second line to "Number".'})}),(0,s.jsx)("li",{children:(0,s.jsxs)(n.p,{children:["Click ",(0,s.jsx)(n.code,{children:"Add policy"}),"."]})})]})})]}),"\n",(0,s.jsxs)(n.p,{children:["The following example uses the ",(0,s.jsx)(n.code,{children:'"all"'})," mode, which is excessive\nand usually unnecessary in clusters of five nodes or larger:"]}),"\n",(0,s.jsxs)(t.A,{groupId:"examples",children:[(0,s.jsxs)(o.A,{value:"bash",label:"bash",default:!0,children:[(0,s.jsxs)(n.admonition,{type:"warning",children:[(0,s.jsx)(n.p,{children:"Mirroring to all nodes is unnecessary and will result\nin unnecessary resource waste."}),(0,s.jsxs)(n.p,{children:['Consider mirroring to the majority (N/2+1) nodes with "ha-mode":"exactly" instead.\nSee ',(0,s.jsx)(n.a,{href:"#replication-factor",children:"Replication Factor"})," above."]})]}),(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'rabbitmqctl set_policy ha-all "^ha\\." \'{"ha-mode":"all"}\'\n'})})]}),(0,s.jsxs)(o.A,{value:"PowerShell",label:"PowerShell",children:[(0,s.jsxs)(n.admonition,{type:"warning",children:[(0,s.jsx)(n.p,{children:"Mirroring to all nodes is unnecessary and will result\nin unnecessary resource waste."}),(0,s.jsxs)(n.p,{children:['Consider mirroring to the majority (N/2+1) nodes with "ha-mode":"exactly" instead.\nSee ',(0,s.jsx)(n.a,{href:"#replication-factor",children:"Replication Factor"})," above."]})]}),(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-PowerShell",children:'rabbitmqctl.bat set_policy ha-all "^ha\\." "{""ha-mode"":""all""}"\n'})})]}),(0,s.jsx)(o.A,{value:"http-api",label:"HTTP API",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:'PUT /api/policies/%2f/ha-two\n{\n "pattern":"^two\\.",\n "definition": {\n "ha-mode":"all",\n "ha-sync-mode":"automatic"\n }\n}\n'})})}),(0,s.jsxs)(o.A,{value:"ui",label:"Management UI",children:[(0,s.jsxs)(n.admonition,{type:"warning",children:[(0,s.jsx)(n.p,{children:"Mirroring to all nodes is unnecessary and will result\nin unnecessary resource waste."}),(0,s.jsxs)(n.p,{children:['Consider mirroring to the majority (N/2+1) nodes with "ha-mode":"exactly" instead.\nSee ',(0,s.jsx)(n.a,{href:"#replication-factor",children:"Replication Factor"})," above."]})]}),(0,s.jsxs)("ul",{children:[(0,s.jsx)("li",{children:(0,s.jsxs)(n.p,{children:["Navigate to ",(0,s.jsx)("code",{children:"Admin"})," > ",(0,s.jsx)("code",{children:"Policies"})," > ",(0,s.jsx)("code",{children:"Add / update a policy"}),"."]})}),(0,s.jsx)("li",{children:(0,s.jsxs)(n.p,{children:['Enter "ha-all" next to Name, "^ha." next to ',(0,s.jsx)(n.code,{children:"Pattern"}),',\nand "ha-mode" = "all" into Definition properties (or press Queues[Classic] -> "HA mode" and enter word "all" into value)']})}),(0,s.jsx)("li",{children:(0,s.jsxs)(n.p,{children:["Click ",(0,s.jsx)("code",{children:"Add policy"}),"."]})})]})]})]}),"\n",(0,s.jsxs)(n.p,{children:['A policy where queues whose names begin with\n"',(0,s.jsx)(n.code,{children:"nodes."}),'" are mirrored to specific nodes in the\ncluster:']}),"\n",(0,s.jsxs)(t.A,{groupId:"examples",children:[(0,s.jsx)(o.A,{value:"bash",label:"bash",default:!0,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'rabbitmqctl set_policy ha-nodes "^nodes\\." \\\n \'{"ha-mode":"nodes","ha-params":["rabbit@nodeA", "rabbit@nodeB"]}\'\n'})})}),(0,s.jsx)(o.A,{value:"PowerShell",label:"PowerShell",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-PowerShell",children:'rabbitmqctl.bat set_policy ha-nodes "^nodes\\." ^\n "{""ha-mode"":""nodes"",""ha-params"":[""rabbit@nodeA"", ""rabbit@nodeB""]}"\n'})})}),(0,s.jsx)(o.A,{value:"http-api",label:"HTTP API",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:'PUT /api/policies/%2f/ha-nodes\n {\n "pattern":"^nodes\\.",\n "definition":{\n "ha-mode":"nodes",\n "ha-params":["rabbit@nodeA", "rabbit@nodeB"]\n }\n'})})}),(0,s.jsx)(o.A,{value:"ui",label:"Management UI",children:(0,s.jsxs)("ul",{children:[(0,s.jsx)("li",{children:(0,s.jsxs)(n.p,{children:["Navigate to ",(0,s.jsx)(n.code,{children:"Admin"})," > ",(0,s.jsx)(n.code,{children:"Policies"})," > ",(0,s.jsx)(n.code,{children:"Add / update a policy"}),"."]})}),(0,s.jsx)("li",{children:(0,s.jsxs)(n.p,{children:['Enter "ha-nodes" next to Name and "^nodes." next to\n',(0,s.jsx)(n.code,{children:"Pattern"}),"."]})}),(0,s.jsx)("li",{children:(0,s.jsx)(n.p,{children:'Enter "ha-mode" = "nodes" in the first line next to\nPolicy, then "ha-params" in the second line, set the\nsecond line\'s type to "List", and then enter\n"rabbit@nodeA" and "rabbit@nodeB" in the sublist which\nappears.'})}),(0,s.jsx)("li",{children:(0,s.jsxs)(n.p,{children:["Click ",(0,s.jsx)(n.code,{children:"Add policy"}),"."]})})]})})]}),"\n",(0,s.jsx)(n.h2,{id:"behaviour",children:"Mirrored Queue Implementation and Semantics"}),"\n",(0,s.jsxs)(n.p,{children:["As discussed, for each mirrored queue there is one\n",(0,s.jsx)(n.em,{children:"leader"})," replica and several ",(0,s.jsx)(n.em,{children:"mirrors"}),", each on a\ndifferent node. The mirrors apply the operations that occur\nto the leader in exactly the same order as the leader and\nthus maintain the same state. All actions other than\npublishes go only to the leader, and the leader then\nbroadcasts the effect of the actions to the mirrors. Thus\nclients consuming from a mirrored queue are in fact\nconsuming from the leader."]}),"\n",(0,s.jsxs)(n.p,{children:["Should a mirror fail, there is little to be done other than\nsome bookkeeping: the leader remains the leader and n
1o\nclient need to take any action or be informed of the failure.\nNote that mirror failures may not be detected immediately and\nthe interruption of the per-connection flow control mechanism\ncan delay message publication. The details are described\nin the ",(0,s.jsx)(n.a,{href:"./nettick",children:"Inter-node Communication Heartbeats"})," guide."]}),"\n",(0,s.jsx)(n.p,{children:"If the leader fails, then one of the mirrors will be promoted to\nleader as follows:"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsxs)(n.li,{children:["The longest running mirror is promoted to leader, the assumption\nbeing that it is most likely to be fully synchronised with the\nleader. If there is no mirror that is ",(0,s.jsx)(n.a,{href:"#unsynchronised-mirrors",children:"synchronised"})," with the\nleader, messages that only existed on leader will be lost."]}),"\n",(0,s.jsx)(n.li,{children:"The mirror considers all previous consumers to have been abruptly\ndisconnected. It requeues all messages that have been delivered\nto clients but are pending acknowledgement. This can include\nmessages for which a client has issued acknowledgements, say, if\nan acknowledgement was either lost on the wire before reaching the\nnode hosting queue leader, or it was lost when broadcast from the leader to the\nmirrors. In either case, the new leader has no choice but to\nrequeue all messages that it has not seen acknowledgements for."}),"\n",(0,s.jsxs)(n.li,{children:["Consumers that have requested to be notified when a queue fails\nover ",(0,s.jsx)(n.a,{href:"#cancellation",children:"will be notified of cancellation"}),"."]}),"\n",(0,s.jsxs)(n.li,{children:["As a result of the requeuing, clients that re-consume from the\nqueue ",(0,s.jsx)("b",{children:"must"})," be aware that they are likely to subsequently\nreceive messages that they have already received."]}),"\n",(0,s.jsx)(n.li,{children:"As the chosen mirror becomes the leader, no messages that are\npublished to the mirrored queue during this time will be lost\n(barring subsequent failures on the promoted node).\nMessages published to a node that hosts queue mirror are routed\nto the queue leader and then replicated to all mirrors. Should the leader fail,\nthe messages continue to be sent to the mirrors and will be added\nto the queue once the promotion of a mirror to the leader completes."}),"\n",(0,s.jsxs)(n.li,{children:["Messages published by clients using ",(0,s.jsx)(n.a,{href:"./confirms",children:"publisher confirms"})," will still be\nconfirmed even if the leader (or any mirrors) fail\nbetween the message being published and a confirmation received\nby the publisher. From the point of view of the publisher,\npublishing to a mirrored queue is no different from publishing to a non-mirrored one."]}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:["If consumers use ",(0,s.jsx)(n.a,{href:"./confirms",children:"automatic acknowledgement mode"}),", then messages can be lost. This is no different\nfrom non-mirrored queues, of course: the broker considers a message\n",(0,s.jsx)(n.em,{children:"acknowledged"})," as soon as it has been sent to a consumer in automatic acknowledgement mode."]}),"\n",(0,s.jsxs)(n.p,{children:["Should the client disconnect abruptly, the message may never be received. In the case of a\nmirrored queue, should the leader die, messages that are in-flight on\ntheir way to consumers in automatic acknowledgement mode may never be received\nby those clients, and will not be requeued by the new leader. Because\nof the possibility that the consuming client is connected to a node\nthat survives, the ",(0,s.jsx)(n.a,{href:"#cancellation",children:"consumer cancellation notification"})," is useful to identify when such events may have\noccurred. Of course, in practise, if data safety is less important\nthan throughput, the automatic acknowledgement mode is the way to go."]}),"\n",(0,s.jsx)(n.h3,{id:"confirms-transactions",children:"Publisher Confirms and Transactions"}),"\n",(0,s.jsxs)(n.p,{children:["Mirrored queues support both ",(0,s.jsx)(n.a,{href:"./confirms",children:"publisher confirms"})," and\ntransactions. The semantics chosen are that in the case of both confirms and\ntransactions, the action spans all mirrors of the\nqueue. So in the case of a transaction, a\n",(0,s.jsx)(n.code,{children:"tx.commit-ok"})," will only be returned to a\nclient when the transaction has been applied across all\nmirrors of the queue. Equally, in the case of publisher\nconfirms, a message will only be confirmed to the\npublisher when it has been accepted by all of the\nmirrors. It is correct to think of the semantics as being\nthe same as a message being routed to multiple normal\nqueues, and of a transaction with publications within\nthat similarly are routed to multiple queues."]}),"\n",(0,s.jsx)(n.h3,{id:"flow-control",children:"Flow Control"}),"\n",(0,s.jsxs)(n.p,{children:["RabbitMQ uses a credit-based algorithm to ",(0,s.jsx)("a",{href:"./memory#per-connection",children:"limit the rate of\nmessage publication"}),". Publishers are permitted to\npublish when they receive credit from all mirrors of a\nqueue. Credit in this context means permission to\npublish. Mirrors that fail to issue credit can cause\npublishers to stall. Publishers will remain blocked until\nall mirrors issue credit or until the remaining nodes\nconsider the mirror to be disconnected from the cluster.\nErlang detects such disconnections by periodically sending\na tick to all nodes. The tick interval can be controlled\nwith the ",(0,s.jsx)(n.a,{href:"./nettick",children:"net_ticktime"}),"\nconfiguration setting."]}),"\n",(0,s.jsx)(n.h3,{id:"cancellation",children:"Leader Failures and Consumer Cancellation"}),"\n",(0,s.jsxs)(n.p,{children:["Clients that are consuming from a mirrored queue may wish\nto know that the queue from which they have been consuming\nhas failed over. When a mirrored queue fails over,\nknowledge of which messages have been sent to which\nconsumer is lost, and therefore all unacknowledged\nmessages are redelivered with the ",(0,s.jsx)(n.code,{children:"redelivered"}),"\nflag set. Consumers may wish to know this is going to\nhappen."]}),"\n",(0,s.jsxs)(n.p,{children:["If so, they can consume with the argument\n",(0,s.jsx)(n.code,{children:"x-cancel-on-ha-failover"})," set to\n",(0,s.jsx)(n.code,{children:"true"}),". Their consuming will then be cancelled\non failover and a ",(0,s.jsx)(n.a,{href:"./consumer-cancel",children:"consumer\ncancellation notification"})," sent. It is then the\nconsumer's responsibility to reissue\n",(0,s.jsx)(n.code,{children:"basic.consume"})," to start consuming again."]}),"\n",(0,s.jsx)(n.p,{children:"For example (in Java):"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-java",children:'Channel channel = ...;\nConsumer consumer = ...;\nMap<String, Object> args = new HashMap<String, Object>();\nargs.put("x-cancel-on-ha-failover", true);\nchannel.basicConsume("my-queue", false, args, consumer);\n'})}),"\n",(0,s.jsx)(n.p,{children:"This creates a new consumer with the argument set."}),"\n",(0,s.jsx)(n.h2,{id:"unsynchronised-mirrors",children:"Unsynchronised Mirrors"}),"\n",(0,s.jsx)(n.p,{children:"A node may join a cluster at any time. Depending on the\nconfiguration of a queue, when a node joins a cluster,\nqueues may add a mirror on the new node. At this point, the\nnew mirror will be empty: it will not contain any existing\ncontents of the queue. Such a mirror will receive new\nmessages published to the queue, and thus over time will\naccurately represent the tail of the mirrored queue. As\nmessages are drained from the mirrored queue, the size of\nthe head of the queue for which the new mirror is missing\nmessages, will shrink until eventually the mirror's contents\nprecisely match the leader's contents. At this point, the\nmirror can be considered fully synchronised, but it is\nimportant to note that this has occurred because of actions\nof clients in terms of draining the pre-existing head of the\nqueue."}),"\n",(0,s.jsx)(n.p,{children:"A newly added mirror provides no additional form of\
1nredundancy or availability of the queue's contents that\nexisted before the mirror was added, unless the queue has\nbeen explicitly synchronised. Since the queue becomes\nunresponsive while explicit synchronisation is occurring, it\nis preferable to allow active queues from which messages are\nbeing drained to synchronise naturally, and only explicitly\nsynchronise inactive queues."}),"\n",(0,s.jsx)(n.p,{children:"When enabling automatic queue mirroring, consider the expected on disk\ndata set of the queues involved. Queues with a sizeable data set\n(say, tens of gigabytes or more) will have to replicate it to\nthe newly added mirror(s), which can put a significant load on\ncluster resources such as network bandwidth and disk I/O. This is\na common scenario with lazy queues, for example."}),"\n",(0,s.jsx)(n.p,{children:"To see mirror status (whether they are synchronised), use:"}),"\n",(0,s.jsxs)(t.A,{groupId:"examples",children:[(0,s.jsx)(o.A,{value:"bash",label:"bash",default:!0,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"rabbitmqctl list_queues name mirror_pids synchronised_mirror_pids\n"})})}),(0,s.jsx)(o.A,{value:"PowerShell",label:"PowerShell",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-PowerShell",children:"rabbitmqctl.bat list_queues name mirror_pids synchronised_mirror_pids\n"})})}),(0,s.jsx)(o.A,{value:"cmd",label:"cmd",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-batch",children:"rabbitmqctl.bat list_queues name mirror_pids synchronised_mirror_pids\n"})})})]}),"\n",(0,s.jsx)(n.p,{children:"It is possible to manually synchronise a queue:"}),"\n",(0,s.jsxs)(t.A,{groupId:"examples",children:[(0,s.jsx)(o.A,{value:"bash",label:"bash",default:!0,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'rabbitmqctl sync_queue "{name}"\n'})})}),(0,s.jsx)(o.A,{value:"PowerShell",label:"PowerShell",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-PowerShell",children:"rabbitmqctl.bat sync_queue '{name}'\n"})})}),(0,s.jsx)(o.A,{value:"cmd",label:"cmd",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-batch",children:'rabbitmqctl.bat sync_queue "{name}"\n'})})})]}),"\n",(0,s.jsx)(n.p,{children:"Or cancel an in-progress synchronisation:"}),"\n",(0,s.jsxs)(t.A,{groupId:"examples",children:[(0,s.jsx)(o.A,{value:"bash",label:"bash",default:!0,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'rabbitmqctl cancel_sync_queue "{name}"\n'})})}),(0,s.jsx)(o.A,{value:"PowerShell",label:"PowerShell",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-PowerShell",children:"rabbitmqctl.bat cancel_sync_queue '{name}'\n"})})}),(0,s.jsx)(o.A,{value:"cmd",label:"cmd",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-batch",children:'rabbitmqctl.bat cancel_sync_queue "{name}"\n'})})})]}),"\n",(0,s.jsx)(n.p,{children:"These features are also available through the management plugin."}),"\n",(0,s.jsx)(n.h3,{id:"promoting-unsynchronised-mirrors",children:"Promotion of Unsynchronised Mirrors on Failure"}),"\n",(0,s.jsxs)(n.p,{children:["By default if a queue's leader node fails, loses\nconnection to its peers or is removed from the cluster,\nthe oldest mirror will be promoted to be the new\nleader. In some circumstances this mirror can be\n",(0,s.jsx)(n.a,{href:"#unsynchronised-mirrors",children:"unsynchronised"}),", which will cause data loss."]}),"\n",(0,s.jsxs)(n.p,{children:["Starting with RabbitMQ 3.7.5, the ",(0,s.jsx)(n.code,{children:"ha-promote-on-failure"}),"\npolicy key controls whether unsynchronised mirror promotion is allowed. When set to\n",(0,s.jsx)(n.code,{children:"when-synced"}),", it will make sure that unsynchronised mirrors\nare not promoted."]}),"\n",(0,s.jsxs)(n.p,{children:["Default value is ",(0,s.jsx)(n.code,{children:"always"}),".\nThe ",(0,s.jsx)(n.code,{children:"when-synced"})," value should be used with care. It trades off\nsafety from unsynchronised mirror promotion for increased reliance on queue leader's\navailability. Sometimes queue availability can be more important than consistency."]}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"when-synced"})," promotion strategy avoids data loss due to promotion of an unsynchronised mirror\nbut makes queue availability dependent on its leader's availability.\nIn the event of queue leader node failure the queue will become unavailable until queue leader\nrecovers. In case of a permanent loss of queue leader the queue won't be available\nunless it is deleted and redeclared. Deleting a queue deletes all of its contents,\nwhich means permanent loss of a leader with this promotion strategy equates to losing all\nqueue contents."]}),"\n",(0,s.jsxs)(n.p,{children:["Systems that use the ",(0,s.jsx)(n.code,{children:"when-synced"})," promotion strategy must use\n",(0,s.jsx)(n.a,{href:"./confirms",children:"publisher confirms"})," in order to detect queue unavailability\nand broker's inability to enqueue messages."]}),"\n",(0,s.jsx)(n.h3,{id:"start-stop",children:"Stopping Nodes and Synchronisation"}),"\n",(0,s.jsxs)(n.p,{children:["If you stop a RabbitMQ node which contains the leader of a\nmirrored queue, some mirror on some other node will be\npromoted to the leader (assuming there is a synchronised mirror;\nsee ",(0,s.jsx)(n.a,{href:"#cluster-shutdown",children:"below"}),"). If you\ncontinue to stop nodes then you will reach a point where a\nmirrored queue has no more mirrors: it exists only on one\nnode, which is now its leader. If the mirrored queue was\ndeclared ",(0,s.jsx)("i",{children:"durable"})," then, if its last remaining node is\nshutdown, durable messages in the queue will survive the\nrestart of that node. In general, as you restart other\nnodes, if they were previously part of a mirrored queue then\nthey will rejoin the mirrored queue."]}),"\n",(0,s.jsxs)(n.p,{children:["However, there is currently no way for a mirror to know\nwhether or not its queue contents have diverged from the\nleader to which it is rejoining (this could happen during a\nnetwork partition, for example). As such, when a mirror\nrejoins a mirrored queue, it throws away any durable local\ncontents it already has and starts empty. Its behaviour is\nat this point the same as if it were a ",(0,s.jsx)("a",{href:"#unsynchronised-mirrors",children:"new node joining the cluster"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"cluster-shutdown",children:"Stopping Nodes Hosting Queue Leader with Only Unsynchronised Mirrors"}),"\n",(0,s.jsx)(n.p,{children:"It's possible that when you shut down a leader node that\nall available mirrors are unsynchronised. A common\nsituation in which this can occur is rolling cluster\nupgrades."}),"\n",(0,s.jsx)(n.p,{children:"By default, RabbitMQ will refuse to promote\nan unsynchronised mirror on controlled leader shutdown\n(i.e. explicit stop of the RabbitMQ service or shutdown of\nthe OS) in order to avoid message loss; instead the entire\nqueue will shut down as if the unsynchronised mirrors were\nnot there."}),"\n",(0,s.jsx)(n.p,{children:"An uncontrolled leader shutdown (i.e. server or\nnode crash, or network outage) will still trigger a\npromotion of an unsynchronised mirror."}),"\n",(0,s.jsxs)(n.p,{children:["If you would prefer to have queue leader move to an\nunsynchronised mirror in all circumstances (i.e. you would\nchoose availability of the queue over avoiding message\nloss due to unsynchronised mirror promotion) then set the\n",(0,s.jsx)(n.code,{children:"ha-promote-on-shutdown"})," policy key to\n",(0,s.jsx)(n.code,{children:"always"}
1)," rather than its default value of\n",(0,s.jsx)(n.code,{children:"when-synced"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["If the ",(0,s.jsx)(n.code,{children:"ha-promote-on-failure"})," policy key is set to\n",(0,s.jsx)(n.code,{children:"when-synced"}),", unsynchronised mirrors will not be promoted\neven if the ",(0,s.jsx)(n.code,{children:"ha-promote-on-shutdown"})," key is set to\n",(0,s.jsx)(n.code,{children:"always"}),". This means that in the event of queue leader node\nfailure the queue will become unavailable until leader recovers.\nIn case of a permanent loss of queue leader the queue won't be available\nunless it is deleted (that will also delete all of its contents) and redeclared."]}),"\n",(0,s.jsxs)(n.p,{children:["Note that ",(0,s.jsx)(n.code,{children:"ha-promote-on-shutdown"})," and\n",(0,s.jsx)(n.code,{children:"ha-promote-on-failure"})," have different default behaviours.\n",(0,s.jsx)(n.code,{children:"ha-promote-on-shutdown"})," is set to ",(0,s.jsx)(n.code,{children:"when-synced"}),"\nby default, while ",(0,s.jsx)(n.code,{children:"ha-promote-on-failure"})," is set to\n",(0,s.jsx)(n.code,{children:"always"})," by default."]}),"\n",(0,s.jsx)(n.h3,{id:"promotion-while-down",children:"Loss of a Leader While All Mirrors are Stopped"}),"\n",(0,s.jsx)(n.p,{children:"It is possible to lose the leader for a queue while all\nmirrors for the queue are shut down. In normal operation\nthe last node for a queue to shut down will become the\nleader, and we want that node to still be the leader when\nit starts again (since it may have received messages that\nno other mirror saw)."}),"\n",(0,s.jsxs)(n.p,{children:["However, when you invoke\n",(0,s.jsx)(n.code,{children:"rabbitmqctl forget_cluster_node"}),', RabbitMQ will attempt to find\na currently stopped mirror for each queue which has its\nleader on the node we are forgetting, and "promote" that\nmirror to be the new leader when it starts up again. If\nthere is more than one candidate, the most recently\nstopped mirror will be chosen.']}),"\n",(0,s.jsxs)(n.p,{children:["It's important to understand that RabbitMQ can only\npromote ",(0,s.jsx)("b",{children:"stopped"})," mirrors during\n",(0,s.jsx)(n.code,{children:"forget_cluster_node"}),', since any mirrors that\nare started again will clear out their contents as\ndescribed at "',(0,s.jsx)(n.a,{href:"#start-stop",children:"stopping nodes and\nsynchronisation"}),'" above. Therefore when removing a lost\nleader in a stopped cluster, you must invoke\n',(0,s.jsx)(n.code,{children:"rabbitmqctl forget_cluster_node"})," ",(0,s.jsx)("i",{children:"before"}),"\nstarting mirrors again."]}),"\n",(0,s.jsx)(n.h2,{id:"batch-sync",children:"Batch Synchronization"}),"\n",(0,s.jsxs)(n.p,{children:["Classic queue leaders perform synchronisation in\nbatches. Batch can be configured via the\n",(0,s.jsx)(n.code,{children:"ha-sync-batch-size"})," queue argument. If no value is set ",(0,s.jsx)(n.code,{children:"mirroring_sync_batch_size"}),"\nis used as the default value. Earlier\nversions (prior to 3.6.0) will synchronise ",(0,s.jsx)(n.code,{children:"1"})," message at a\ntime by default. By synchronising messages in batches,\nthe synchronisation process can be sped up considerably."]}),"\n",(0,s.jsxs)(n.p,{children:["To choose the right value for\n",(0,s.jsx)(n.code,{children:"ha-sync-batch-size"})," you need to consider:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"average message size"}),"\n",(0,s.jsx)(n.li,{children:"network throughput between RabbitMQ nodes"}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"net_ticktime"})," value"]}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:["For example, if you set ",(0,s.jsx)(n.code,{children:"ha-sync-batch-size"})," to\n",(0,s.jsx)(n.code,{children:"50000"})," messages, and each message in the\nqueue is 1KB, then each synchronisation message between nodes\nwill be ~49MB. You need to make sure that your network\nbetween queue mirrors can accommodate this kind of traffic. If the\nnetwork takes longer than ",(0,s.jsx)(n.a,{href:"./nettick",children:"net_ticktime"}),"\nto send one batch of messages, then nodes in the cluster could\nthink they are in the presence of a network partition."]}),"\n",(0,s.jsxs)(n.p,{children:["The amount of data sent over the network can also be c
1ontrolled by setting\nthe parameter ",(0,s.jsx)(n.code,{children:"mirroring_sync_max_throughput"}),". The parameter specifies the\nnumber of bytes per second that is being transferred. The default is ",(0,s.jsx)(n.code,{children:"0"}),", which disables\nthis feature."]}),"\n",(0,s.jsx)(n.h3,{id:"configuring-synchronisation",children:"Configuring Synchronisation"}),"\n",(0,s.jsxs)(n.p,{children:["Let's start with the most important aspect of queue\nsynchronisation: ",(0,s.jsx)("em",{children:"while a queue is being synchronised, all other\nqueue operations will be blocked"}),". Depending on multiple\nfactors, a queue might be blocked by synchronisation for many\nminutes or hours, and in extreme cases even days."]}),"\n",(0,s.jsx)(n.p,{children:"Queue synchronisation can be configured as follows:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"ha-sync-mode: manual"}),": this is the default mode.\nA new queue mirror will not receive existing messages, it will\nonly receive new messages. The new queue mirror will become an\nexact replica of the leader over time, once consumers have\ndrained messages that only exist on the leader. If the leader\nqueue fails before all unsynchronised messages are drained,\nthose messages will be lost. You can fully synchronise a queue\nmanually, refer to ",(0,s.jsx)(n.a,{href:"#unsynchronised-mirrors",children:"unsynchronised mirrors"}),"\nsection for details."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"ha-sync-mode: automatic"}),": a queue will\nautomatically synchronise when a new mirror joins. It is worth\nreiterating that queue synchronisation is a blocking operation.\nIf queues are small, or you have a fast network between\nRabbitMQ nodes and the ",(0,s.jsx)(n.code,{children:"ha-sync-batch-size"})," was\noptimised, this is a good choice."]}),"\n"]})]})}function m(e={}){let{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(u,{...e})}):u(e)}},22326(e,n,r){r.d(n,{A:()=>i});let i=r.p+"assets/images/queue_mirroring_indicators_management_ui_row_only-ccaea71cc4235863bd01cc86e324ae21.png"},8627(e,n,r){r.d(n,{A:()=>i});let i=r.p+"assets/images/queue_mirroring_no_mirrors-69bfbd5bf9411faa6fa7e8fe27cb77a0.png"},83069(e,n,r){r.d(n,{A:()=>i});let i=r.p+"assets/images/queue_mirroring_one_mirror_present-a2483a78f01e0813ddbf1c70ce840f67.png"},57250(e,n,r){r.d(n,{A:()=>o});var i=r(74848);r(96540);var s=r(34164),a=r(57663);function t({children:e,className:n,hidden:r}){return(0,i.jsx)("div",{role:"tabpanel",className:(0,s.A)("tabItem_Ymn6",n),hidden:r,children:e})}function o({children:e,className:n,value:r}){let{selectedValue:s,lazy:l}=(0,a.uc)(),d=r===s;return!d&&l?null:(0,i.jsx)(t,{className:n,hidden:!d,children:e})}},50773(e,n,r){r.d(n,{A:()=>u});var i=r(74848);r(96540);var s=r(34164),a=r(88287),t=r(57663),o=r(28584),l=r(19863);function d({className:e}){let{selectedValue:n,selectValue:r,tabValues:a,block:l}=(0,t.uc)(),c=[],{blockElementScrollPositionUntilNextRender:h}=(0,o.a_)(),u=e=>{let i=e.currentTarget,s=a[c.indexOf(i)].value;s!==n&&(h(i),r(s))},m=e=>{let n=null;switch(e.key){case"Enter":u(e);break;case"ArrowRight":{let r=c.indexOf(e.currentTarget)+1;n=c[r]??c[0];break}case"ArrowLeft":{let r=c.indexOf(e.currentTarget)-1;n=c[r]??c[c.length-1]}}n?.focus()};return(0,i.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":l},e),children:a.map(({value:e,label:r,attributes:a})=>(0,i.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{c.push(e)},onKeyDown:m,onClick:u,...a,className:(0,s.A)("tabs__item","tabItem_LNqP",a?.className,{"tabs__item--active":n===e}),children:r??e},e))})}function c({children:e}){return(0,i.jsx)("div",{className:"margin-top--md",children:e})}function h({className:e,children:n}){return(0,i.jsxs)("div",{className:(0,s.A)(a.G.tabs.container,"tabs-container","tabList__CuJ"),children:[(0,i.jsx)(d,{className:e}),(0,i.jsx)(c,{children:n})]})}function u(e){let n=(0,l.A)(),r=(0,t.OC)(e);return(0,i.jsx)(t.O_,{value:r,children:(0,i.jsx)(h,{className:e.className,children:(0,t.vT)(e.children)})},String(n))}},57663(e,n,r){r.d(n,{OC:()=>u,O_:()=>b,uc:()=>p,vT:()=>c});var i=r(74848),s=r(96540),a=r(56347),t=r(99989),o=r(96629),l=r(80618),d=r(41367);function c(e){return s.Children.toArray(e).filter(e=>"\n"!==e)}function h({value:e,tabValues:n}){return n.some(n=>n.value===e)}function u(e){let n,{defaultValue:r,queryString:i=!1,groupId:c}=e,u=function(e){let{values:n,children:r}=e;return(0,s.useMemo)(()=>{let e=n??s.Children.toArray(r).flatMap(e=>{if(!e)return[];if((0,s.isValidElement)(e)&&function(e){let{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return[e];
1let n="string"==typeof e.type?e.type:e.type.name;throw Error(`Docusaurus error: Bad <Tabs> child <${n}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop. 2If you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.`)}).map(({props:{value:e,label:n,attributes:r,default:i}})=>({value:e,label:n,attributes:r,default:i})),i=(0,l.XI)(e,(e,n)=>e.value===n.value);if(i.length>0)throw Error(`Docusaurus error: Duplicate values "${i.map(e=>`'${e.value}'`).join(", ")}" found in <Tabs>. Every value needs to be unique.`);return e},[n,r])}(e),[m,p]=(0,s.useState)(()=>(function({defaultValue:e,tabValues:n}){if(0===n.length)throw Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!h({value:e,tabValues:n}))throw Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${n.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}let r=n.find(e=>e.default)??n[0];if(!r)throw Error("Unexpected error: 0 tabValues");return r.value})({defaultValue:r,tabValues:u})),[b,x]=function({queryString:e=!1,groupId:n}){let r=(0,a.W6)(),i=function({queryString:e=!1,groupId:n}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!n)throw Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:e,groupId:n});return[(0,o.aZ)(i),(0,s.useCallback)(e=>{if(!i)return;let n=new URLSearchParams(r.location.search);n.set(i,e),r.replace({...r.location,search:n.toString()})},[i,r])]}({queryString:i,groupId:c}),[f,g]=function({groupId:e}){let n=e?`docusaurus.tab.${e}`:null,[r,i]=(0,d.Dv)(n);return[r,(0,s.useCallback)(e=>{n&&i.set(e)},[n,i])]}({groupId:c}),j=h({value:n=b??f,tabValues:u})?n:null;return(0,t.A)(()=>{j&&p(j)},[j]),{selectedValue:m,selectValue:(0,s.useCallback)(e=>{if(!h({value:e,tabValues:u}))throw Error(`Can't select invalid tab value=${e}`);p(e),x(e),g(e)},[x,g,u]),tabValues:u,lazy:e.lazy??!1,block:e.block??!1}}let m=(0,s.createContext)(null);function p(){let e=s.useContext(m);if(!e)throw Error("useTabsContext() must be used within a Tabs component");return e}function b(e){return(0,i.jsx)(m.Provider,{value:e.value,children:e.children})}},28453(e,n,r){r.d(n,{R:()=>t,x:()=>o});var i=r(96540);let s={},a=i.createContext(s);function t(e){let n=i.useContext(a);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:t(e.components),i.createElement(a.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.