galera-4-26.4.22/debian/000755 000162 177776 00000000000 14755062445 015737 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/debian/galera-4.links000644 000162 177776 00000000071 14755062442 020370 0ustar00jenkinsnogroup000000 000000 usr/lib/libgalera_smm.so usr/lib/galera/libgalera_smm.so galera-4-26.4.22/debian/galera-arbitrator-4.garb.init000777 000162 177776 00000000000 14755062445 026775 2../garb/files/garb.shustar00jenkinsnogroup000000 000000 galera-4-26.4.22/debian/galera-4.install000644 000162 177776 00000000031 14755062442 020712 0ustar00jenkinsnogroup000000 000000 usr/lib/libgalera_smm.so galera-4-26.4.22/debian/upstream/000755 000162 177776 00000000000 14755062445 017577 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/debian/upstream/metadata000644 000162 177776 00000000337 14755062442 021302 0ustar00jenkinsnogroup000000 000000 Bug-Database: https://github.com/codership/galera/issues Bug-Submit: https://github.com/codership/galera/issues/new Repository: https://github.com/codership/galera.git Repository-Browse: https://github.com/codership/galera galera-4-26.4.22/debian/upstream/signing-key.asc000644 000162 177776 00000003252 14755062442 022512 0ustar00jenkinsnogroup000000 000000 -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1 mQENBFSC9PABCADViCrLIvhcuppbuFaCRiZYLvCoZ8st9uMwrofvbVbokWFZgwq/ tGzCi4o2O042hZS1q9FrB5au8u/Hj/NtGHLXpuSo6IvsAE+dkhSOKvBHxjvVLhbR WYA4EmHAMiCVTYI71MGSrsWcAm0aBwtieXiMavo46cjscQGuXh2qiHuH8UQk3sW0 1yHngS27zg5tXHa1OXL1AwN9fnINoyHfMv0Qtt7xVjTW8YCJqESuK8rVAhI53hBM FEhVRkay2y07olHXD40Q1/+pZ/Bzo5t6VZVOidiml/Euu2QWEvVKVKEe8zhEXkJr ZdwTe4gIlsF78gD6uycllurMl0QFM+uhcUyhABEBAAG0JUNvZGVyc2hpcCBPeSA8 aW5mb0BnYWxlcmFjbHVzdGVyLmNvbT6JATgEEwECACIFAlSC9PACGwMGCwkIBwMC BhUIAgkKCwQWAgMBAh4BAheAAAoJENZpAX68Gd26ovwIAMEm1mBMvMjrB9N6vbZx nuxWmbW2IqINbL5oSsk7+wn0tZteywk+BWNuiU7mVVChuIRl/PtbETUyJkf7SDNx qk7JGtMjL5fZZ/mcwC/vCLVjJHIgkuFodqnnJ40kp2+pwrn5+Xp5H6Z6dLwTBFv4 IW34SgKlH63+xYVPImBsse8a9b7UwevWpoY+NNGDTaGdvGk6hJd2teuU5f7DbVe9 9WFbdoLt4f0ZZhEW1IWbyW4s6JWrOhnd7EpzrSzwAA+MyETbGkyXBmaAybYGtE3/ XdZzyvQcgBkYHOVHy/AJnW6bRrTfiUVGqF5TCFbTAcw0flwV4PkyJVszbhySRhUh oHS5AQ0EVIL08AEIANKaVOsr6NVO1Dnn367JiXf+52dAMcuIvd8y9UY7RP3ONdRB XbwmnknQDrVTAblWxH3lZdsjQwJkmM7l0ryKWhJMYsarbjIHTkKXdT5juskm0PXg gPj+xJm9ejNBWj7IpPicILBJmvIs3DtQ9LGYkyxCmABBycw0OkXiJAODj3AB2f7+ D7PEMwwGH1KZDPD5PX54IXThHwzsakRa2qApyk8/K8NN6uBOhVXSx4MAbzTwf5U6 zRlN/HO6Rd2vRBA15VkaJWwPlQAGSVLyApkUB/CXh7bwB0Bdp2ttZnaYVI4IHqyB Vi5b0VY1bBYZplGhnB91avSX/jiL1fQVnnbOAOMAEQEAAYkBHwQYAQIACQUCVIL0 8AIbDAAKCRDWaQF+vBnduqSlCADPD52hKg1DNmBRJ3dHM5qx3K5evbGTsyi5G2ol G0C03VJbovZ6UTI+34dsw5YaRfxHmjsO7sH+PAk/4F+xOHz+Q7I7MRITTgV+tlcl LSE6riAFllfIvCm2zPwViUwLJfurqINOke010szCw+gynJgp40M1YFGwu2wSeWaN 7SO8eIe7BqaJ0tgh8INLbh2QhLpvszZTWrKEOE7ReLwzo9uB7Le09D3uTussGrNk 3/lvP1T0Y+McC3Js00nprdUgk17tDDmt+ikl2IWO02litImXQexEeRoA1YiagJX0 CCJXr6iFgKG+7gpDxa0H49GuJPK86jYQi2/0CQeSCnWWZkJq =Qon/ -----END PGP PUBLIC KEY BLOCK----- galera-4-26.4.22/debian/galera-arbitrator-4.lintian-overrides000644 000162 177776 00000000325 14755062442 025057 0ustar00jenkinsnogroup000000 000000 # garbd is intentionally not started automatically upon installation, # see https://github.com/codership/galera/issues/266 galera-arbitrator-4: script-in-etc-init.d-not-registered-via-update-rc.d etc/init.d/garb galera-4-26.4.22/debian/copyright000644 000162 177776 00000062270 14755062442 017676 0ustar00jenkinsnogroup000000 000000 Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Galera replication - a write set (wsrep) provider Upstream-Contact: Codership Oy Source: https://github.com/codership/galera Comment: Codership have added an additional clause to cover use of OpenSSL with Galera under the GPL-2 license. This is committed to the 2.x branch upstream: . https://bazaar.launchpad.net/~codership/galera/2.x/revision/167 Files: * Copyright: 2007-2015 Codership Oy License: GPL-2 On Debian based systems the full text of the GNU General Public License version 2 can be found in the file `/usr/share/common-licenses/GPL-2`. . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same license as the "OpenSSL" library), and distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than "OpenSSL". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. Files: asio/* Copyright: 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com), 2008 Rep Invariant Systems, Inc. (info@repinvariant.com), 2005 Stefan Arentz (stefan at soze dot com) License: other Boost Software License - Version 1.0 - August 17th, 2003 . Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: . The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: docs/* Copyright: 2014 Codership Oy 2007-2011 by the Sphinx team, see AUTHORS License: GFDL-1.1+ or CC-BY-SA-3.0 License: GFDL-1.1+ Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License (GFDL), Version 1.1 or any later version published by the Free Software Foundation with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. . The full text of the GFDL is distributed as in /usr/share/common-licenses/GFDL-1.2 on Debian systems. License: CC-BY-SA-3.0 Creative Commons Legal Code . Attribution-ShareAlike 3.0 Unported . CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. . License . THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. . BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. . 1. Definitions . a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License. c. "Creative Commons Compatible License" means a license that is listed at https://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License. d. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. e. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike. f. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. g. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. h. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. i. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. j. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. k. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. . 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. . 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: . a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, d. to Distribute and Publicly Perform Adaptations. e. For the avoidance of doubt: . i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. . The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. . 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: . a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested. b. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License. c. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. d. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. . 5. Representations, Warranties and Disclaimer . UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. . 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. . 7. Termination . a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. . 8. Miscellaneous . a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. . . Creative Commons Notice . Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. . Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of the License. . Creative Commons may be contacted at https://creativecommons.org/. galera-4-26.4.22/debian/galera-arbitrator-4.manpages000644 000162 177776 00000000037 14755062442 023214 0ustar00jenkinsnogroup000000 000000 man/garb-systemd.8 man/garbd.8 galera-4-26.4.22/debian/gbp.conf000644 000162 177776 00000000323 14755062442 017351 0ustar00jenkinsnogroup000000 000000 [DEFAULT] # Ignore requirement to use branch name 'master' to make it easier # for contributors to work with feature and bugfix branches ignore-branch = True # native package configuration pristine-tar = False galera-4-26.4.22/debian/control000644 000162 177776 00000004624 14755062442 017345 0ustar00jenkinsnogroup000000 000000 Source: galera-4 Maintainer: Codership Oy Uploaders: Otto Kekäläinen Section: database Priority: optional Standards-Version: 4.5.0 Rules-Requires-Root: no Build-Depends: check, cmake (>= 2.8), debhelper (>= 10), libasio-dev, libboost-dev (>= 1.41), libboost-program-options-dev (>= 1.41), libssl-dev Homepage: https://www.galeracluster.com/ Vcs-Git: https://github.com/codership/galera.git Vcs-Browser: https://github.com/codership/galera Package: galera-4 Architecture: any Section: libs Depends: ${misc:Depends}, ${shlibs:Depends} Conflicts: galera-3, garbd-2, garbd-3, garbd2, garbd3, percona-galera-3, percona-galera-4, percona-xtradb-cluster-galera, percona-xtradb-cluster-galera-2.x, percona-xtradb-cluster-galera-3.x, percona-xtradb-cluster-galera-4.x, percona-xtradb-cluster-garbd-2.x, percona-xtradb-cluster-garbd-3.x Breaks: galera Replaces: galera Provides: galera, galera4, percona-xtradb-cluster-galera-26, wsrep Description: Replication framework for transactional applications Galera is a fast synchronous multimaster wsrep provider (replication engine) for transactional databases and similar applications. For more information about wsrep API see https://github.com/codership/wsrep-API. For a description of Galera replication engine see http://galeracluster.com. . This package contains the Galera library/plugin. Package: galera-arbitrator-4 Architecture: any Conflicts: galera-arbitrator-3, garbd-2, garbd2, percona-galera-arbitrator-3, percona-galera-arbitrator-4, percona-xtradb-cluster-garbd-2.x Breaks: percona-xtradb-cluster-galera-2.x Replaces: percona-xtradb-cluster-galera-2.x Depends: lsb-base (>= 3.0-6), ${misc:Depends}, ${shlibs:Depends} Description: Galera arbitrator daemon Galera is a fast synchronous multimaster wsrep provider (replication engine) for transactional databases and similar applications. For more information about wsrep API see https://github.com/codership/wsrep-API. For a description of Galera replication engine see http://galeracluster.com. . This package contains the Galera arbitrator daemon (garbd). galera-4-26.4.22/debian/source/000755 000162 177776 00000000000 14755062445 017237 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/debian/source/format000644 000162 177776 00000000015 14755062442 020443 0ustar00jenkinsnogroup000000 000000 3.0 (native) galera-4-26.4.22/debian/galera-arbitrator-4.garb.service000777 000162 177776 00000000000 14755062445 030520 2../garb/files/garb.serviceustar00jenkinsnogroup000000 000000 galera-4-26.4.22/debian/compat000644 000162 177776 00000000003 14755062442 017133 0ustar00jenkinsnogroup000000 000000 10 galera-4-26.4.22/debian/changelog000644 000162 177776 00000000214 14755062442 017603 0ustar00jenkinsnogroup000000 000000 galera-4 (26.4.22) UNRELEASED; urgency=medium * Galera 4 release -- Codership Oy Mon, 17 Feb 2025 14:21:03 +0200 galera-4-26.4.22/debian/README.Maintainer000644 000162 177776 00000000245 14755062442 020703 0ustar00jenkinsnogroup000000 000000 Build the native Debian packaging using: $ cd galera $ git clean -d -f && git reset --hard # clean up any cruft from previous builds $ dpkg-buildpackage -us -uc -b galera-4-26.4.22/debian/galera-arbitrator-4.garb.default000777 000162 177776 00000000000 14755062445 027612 2../garb/files/garb.cnfustar00jenkinsnogroup000000 000000 galera-4-26.4.22/debian/rules000755 000162 177776 00000001207 14755062442 017014 0ustar00jenkinsnogroup000000 000000 #!/usr/bin/make -f # Enable Debian Hardening # https://wiki.debian.org/Hardening export DEB_BUILD_MAINT_OPTIONS = hardening=+all DPKG_EXPORT_BUILDFLAGS = 1 # Include all defaults, includes buildflags.mk include /usr/share/dpkg/default.mk # Set unit test timeout multiplier to make slow tests # pass on loaded builders. export CK_TIMEOUT_MULTIPLIER=5 override_dh_auto_test: dh_auto_test -O--no-parallel -- ARGS=--output-on-failure # Start earlier than MySQL which has value 19 override_dh_installinit-arch: dh_installinit -n --name=garb -- defaults 18 22 override_dh_installsystemd: dh_installsystemd --name=garb %: dh $@ --list-missing galera-4-26.4.22/debian/galera-4.docs000644 000162 177776 00000000040 14755062442 020174 0ustar00jenkinsnogroup000000 000000 AUTHORS scripts/packages/README galera-4-26.4.22/debian/galera-arbitrator-4.install000644 000162 177776 00000000056 14755062442 023070 0ustar00jenkinsnogroup000000 000000 garb/files/garb-systemd usr/bin usr/bin/garbd galera-4-26.4.22/.whitesource000644 000162 177776 00000000212 14755062442 017047 0ustar00jenkinsnogroup000000 000000 { "checkRunSettings": { "vulnerableCheckRunConclusionLevel": "failure" }, "issueSettings": { "minSeverityLevel": "LOW" } }galera-4-26.4.22/common/000755 000162 177776 00000000000 14755062445 016005 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/common/common.h000644 000162 177776 00000001005 14755062442 017437 0ustar00jenkinsnogroup000000 000000 /* *Copyright (C) 2012-2014 Codership Oy */ /*! @file Stores some common definitions to be known throughout the modules */ #ifndef COMMON_DEFS_H #define COMMON_DEFS_H #define COMMON_BASE_HOST_KEY "base_host" #define COMMON_BASE_PORT_KEY "base_port" #define COMMON_BASE_PORT_DEFAULT "4567" #define COMMON_BASE_DIR_KEY "base_dir" #define COMMON_BASE_DIR_DEFAULT "." #define COMMON_STATE_FILE "grastate.dat" #define COMMON_VIEW_STAT_FILE "gvwstate.dat" #endif // COMMON_DEFS_H galera-4-26.4.22/.jenkins/000755 000162 177776 00000000000 14755062445 016234 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/.jenkins/aws-galera-4-fullbuild.groovy000644 000162 177776 00000005233 14755062442 023647 0ustar00jenkinsnogroup000000 000000 pipeline { agent none stages { stage ('Build sourcetar') { steps { script { def sourceJob = build job: 'aws-galera-4-sourcetar', wait: true, parameters: [ string(name: 'GIT_TARGET', value: env.GIT_TARGET ), booleanParam( name: 'HOTFIX_BUILD', value: env.HOTFIX_BUILD) ] env.SRCTAR_JOB = sourceJob.getNumber().toString() } } } stage ('Build binary packages') { parallel { stage ('Build bintar') { steps { script { def bintarJob = build job: 'aws-galera-4-bintar', wait: true, parameters: [string(name: 'BUILD_SELECTOR', value: env.SRCTAR_JOB )] env.BINTAR_JOB = bintarJob.getNumber().toString() } } } stage ('Build rpm packages') { steps { script { def rpmJob = build job: 'aws-galera-4-rpm-packages', wait: true, parameters: [string(name: 'BUILD_SELECTOR', value: env.SRCTAR_JOB )] env.RPM_JOB = rpmJob.getNumber().toString() } } } stage ('Build deb packages') { steps { script { def debJob = build job: 'aws-galera-4-deb-packages', wait: true, parameters: [string(name: 'BUILD_SELECTOR', value: env.SRCTAR_JOB )] env.DEB_JOB = debJob.getNumber().toString() } } } } // parallel } // Build binary packages stage ('Run tests') { parallel { stage('Run bintar test') { steps { build job: 'run-galera-4-release-test', wait: true, parameters: [string(name: 'BUILD_SELECTOR', value: env.BINTAR_JOB )] } } stage ('Run RPM test') { steps { build job: 'run-galera-4-rpm-test', wait: true, parameters: [string(name: 'BUILD_SELECTOR', value: env.RPM_JOB )] } } stage ('Run DEB test') { steps { build job: 'run-galera-4-deb-test', wait: true, parameters: [string(name: 'BUILD_SELECTOR', value: env.DEB_JOB )] } } stage ('Run SST RPM test') { steps { build job: 'run-galera-4-systemd-sst-rpm-test', wait: true, parameters: [string(name: 'BUILD_SELECTOR', value: env.RPM_JOB )] } } stage ('Run SST DEB test') { steps { build job: 'run-galera-4-systemd-sst-deb-test', wait: true, parameters: [string(name: 'BUILD_SELECTOR', value: env.DEB_JOB )] } } } // parallel } } // stages } galera-4-26.4.22/.jenkins/pr-galera-4.x-review.groovy000644 000162 177776 00000003105 14755062442 023257 0ustar00jenkinsnogroup000000 000000 // pipeline { agent { label 'built-in' } stages { stage ('Smoke Test') { steps { script { def res = sh(script: "echo -e \"$ghprbPullLongDescription\" | grep '^MYSQL_BRANCH=' ||:", returnStdout: true) if (!res.isEmpty()) { env.MYSQL_BRANCH = res.split('=')[1].trim() } else { env.MYSQL_BRANCH = env.DEFAULT_MYSQL_BRANCH } echo sh(script: 'env|sort', returnStdout: true) build job: 'pr-galera-4.x-smoke-test', wait: true, parameters: [ string(name: 'GALERA_BRANCH', value: env.ghprbActualCommit )] } } } stage ('Build') { steps { script { def bintarJob = build job: 'pr-build-galera-4.x-mysql-8.0-v26', wait: true, parameters: [ string(name: 'GALERA_BRANCH', value: env.ghprbActualCommit ), string(name: 'MYSQL_BRANCH', value: env.MYSQL_BRANCH) ] env.BINTAR_JOB = bintarJob.getNumber().toString() } } } stage ('Run Tests') { parallel { stage ('MTR') { steps { build job: 'pr-mtr-galera-4.x-mysql-8.0-v26', wait: true, parameters: [ string(name: 'BUILD_SELECTOR', value: env.BINTAR_JOB) ] } } stage ('GcTest') { steps { build job: 'pr-sssc-galera-4.x-mysql-8.0-v26', wait: true, parameters: [ string(name: 'BUILD_SELECTOR', value: env.BINTAR_JOB) ] } } } // parallel } } } galera-4-26.4.22/Testing/000755 000162 177776 00000000000 14755062445 016132 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/Testing/Temporary/000755 000162 177776 00000000000 14755062445 020114 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/LICENSE000644 000162 177776 00000043254 14755062442 015527 0ustar00jenkinsnogroup000000 000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. galera-4-26.4.22/tests/000755 000162 177776 00000000000 14755062445 015657 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/tests/run_sqlgen.sh000755 000162 177776 00000001541 14755062442 020371 0ustar00jenkinsnogroup000000 000000 #!/bin/bash -eu declare -r DIST_BASE=$(cd $(dirname $0); pwd -P) TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . $TEST_BASE/conf/main.conf declare -r SCRIPTS="$DIST_BASE/scripts" . $SCRIPTS/jobs.sh . $SCRIPTS/action.sh . $SCRIPTS/kill.sh . $SCRIPTS/misc.sh check() { consistency_check $sqlgen_pid } #trap check SIGINT #node=1 #DBMS_HOST=${NODE_INCOMING_HOST[$node]} #DBMS_PORT=${NODE_INCOMING_PORT[$node]} # Start load SQLGEN=${SQLGEN:-"$DIST_BASE/bin/sqlgen"} LD_PRELOAD=$GLB_PRELOAD \ DYLD_INSERT_LIBRARIES=$GLB_PRELOAD \ DYLD_FORCE_FLAT_NAMESPACE=1 \ $SQLGEN --user $DBMS_TEST_USER --pswd $DBMS_TEST_PSWD --host $DBMS_HOST \ --port $DBMS_PORT --users $DBMS_CLIENTS --duration 999999999 \ --stat-interval 20 --sess-min 999999 --sess-max 999999 \ --rollbacks 0.1 # >/dev/null 2>$BASE_RUN/seesaw.err & #declare -r sqlgen_pid=$! #fg galera-4-26.4.22/tests/run.sh000755 000162 177776 00000002632 14755062442 017022 0ustar00jenkinsnogroup000000 000000 #!/bin/bash set -e # This script assumes that galera cluster is alredy installed and configured # This is location of this script. _HOME suffix preferred to _ROOT to avoid # confusion THIS_HOME=$(cd $(dirname $0); pwd -P) # Optional configuration file if test -n "$GALERA_TEST_CONFIG" then . "$GALERA_TEST_CONFIG" fi GALERA_TESTS_HOME=${GALERA_TESTS_HOME:-$THIS_HOME} GALERA_RESULTS_HOME=${GALERA_RESULTS_HOME:-$GALERA_TESTS_HOME/results} # Incoming cluster address (load balancer) export GALERA_CLUSTER_IP=${GALERA_CLUSTER_IP:-"127.0.0.1"} export GALERA_CLUSTER_PORT=${GALERA_CLUSTER_PORT:-3306} # List of tests to run GALERA_TESTS=${GALERA_TESTS:-"sqlgen dbt2 dots"} # This is needed for native load balancing and consistency checking export GALERA_NODES_IPS=${GALERA_NODE_IPS:?"GALERA_NODE_IPS not set"} export GALERA_NODES_PORTS=${GALERA_NODE_PORTS:?"GALERA_NODE_PORTS not set"} # Create a results directory for this run GALERA_DATE=$(date +%Y-%m-%d_%H:%M:%S) mkdir -p $GALERA_RESULTS_HOME/$GALERA_DATE declare TESTS_FAILED TESTS_FAILED=0 for TEST in $GALERA_TESTS do export GALERA_RESULT_DIR=$GALERA_RESULTS_HOME/$GALERA_DATE/$TEST mkdir -p $GALERA_RESULT_DIR echo -n "Running $TEST... " $GALERA_TESTS_HOME/test_$TEST/run.sh && echo "passed" \ || { TESTS_FAILED=$[ $TESTS_FAILED + 1 ]; echo "failed"; } done if [ $TESTS_FAILED != "0" ] then echo "Tests failed: $TESTS_FAILED" exit 1 fi # galera-4-26.4.22/tests/run_test_set.sh000755 000162 177776 00000000642 14755062442 020733 0ustar00jenkinsnogroup000000 000000 #!/bin/sh # # This is a wrapper script for ./tap/run_test_set.pl # BASE_DIR=$(cd $(dirname $0); pwd -P) cd $BASE_DIR res=$? if test $res != 0 then echo "Failed to change directory to $BASE_DIR" exit 1 fi export TEST_BASE_DIR=$BASE_DIR . $BASE_DIR/conf/cluster.conf $BASE_DIR/tap/run_test_set.pl $@ res=$? if test $res != 0 then echo "Failed to run test set, exit code: $res" exit 1 fi exit 0 galera-4-26.4.22/tests/conf/000755 000162 177776 00000000000 14755062445 016604 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/tests/conf/galera-ca.pem000644 000162 177776 00000003365 14755062442 021127 0ustar00jenkinsnogroup000000 000000 -----BEGIN CERTIFICATE----- MIIE9jCCA96gAwIBAgIUeSFiqOH8MAdfxA+Wb5QFN6tSvDMwDQYJKoZIhvcNAQEL BQAwgaExCzAJBgNVBAYTAkZJMRAwDgYDVQQIDAdVdXNpbWFhMREwDwYDVQQHDAhI ZWxzaW5raTEVMBMGA1UECgwMQ29kZXJzaGlwIE95MRUwEwYDVQQLDAxHYWxlcmEg RGV2ZWwxFzAVBgNVBAMMDkdhbGVyYSBSb290IENBMSYwJAYJKoZIhvcNAQkBFhdk ZXZlbEBnYWxlcmFjbHVzdGVyLmNvbTAeFw0yMTAxMjExMDMwMDZaFw0zMTAxMTkx MDMwMDZaMIGhMQswCQYDVQQGEwJGSTEQMA4GA1UECAwHVXVzaW1hYTERMA8GA1UE BwwISGVsc2lua2kxFTATBgNVBAoMDENvZGVyc2hpcCBPeTEVMBMGA1UECwwMR2Fs ZXJhIERldmVsMRcwFQYDVQQDDA5HYWxlcmEgUm9vdCBDQTEmMCQGCSqGSIb3DQEJ ARYXZGV2ZWxAZ2FsZXJhY2x1c3Rlci5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB DwAwggEKAoIBAQDMIRmpl7aEqxnbnl6shlFTMvIUexTj15guXvRD5XHLxBcWgs7O cdb1gnZJWk+S1hlvxZZl3qVsxGbk2TPvbmDkCmJjm4NsT0OP8DRnjG+vrb+r04Q4 eItqSKryA5K/Tx1mWR6Fli5gwr8m054ZqJqAn84x8YlcoXswmOI4mbhTxP6H8S/S D14xlusZgDyZZJkZdC31zIsth1Z4+v7X5HzNvkSD1Rf0NkLtYsBZA57dTsiQ+pYr QNcrxa2GEisWqFgttQLNy2FIYNIoS4fr6hxDaGMaAopSdnHkMiIr6WMIDCb1SOC4 JQwAI29dpFlEfg2DIB+kkN6hd8cHvRi7idTTAgMBAAGjggEiMIIBHjAdBgNVHQ4E FgQUxBvY+4ary5U4O2s6Xm8a+E//SvAwgeEGA1UdIwSB2TCB1oAUxBvY+4ary5U4 O2s6Xm8a+E//SvChgaekgaQwgaExCzAJBgNVBAYTAkZJMRAwDgYDVQQIDAdVdXNp bWFhMREwDwYDVQQHDAhIZWxzaW5raTEVMBMGA1UECgwMQ29kZXJzaGlwIE95MRUw EwYDVQQLDAxHYWxlcmEgRGV2ZWwxFzAVBgNVBAMMDkdhbGVyYSBSb290IENBMSYw JAYJKoZIhvcNAQkBFhdkZXZlbEBnYWxlcmFjbHVzdGVyLmNvbYIUeSFiqOH8MAdf xA+Wb5QFN6tSvDMwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcN AQELBQADggEBACt0h/vwwQLYcU5h3JQ/aQSayl1BpGyzwasKjcpjOut1peTHQG+J 0Znu/+YFhU+eTF8cbxBT4dAtHqOno+RVoDcroyDb+mdyN+5+j/ks+pN5aZ8GnsM9 IWx8fSqSu8LCU/AJt3hOwmPHRBThgBHupOBstycjZsh+s7mCt04CVC+O0ypwFe5j Fzq8bWed0rc/kfdqWTVcW3spo8OkSNV6BwPBa2UqjyrFcStCjZcBhLvw8K6qBfzC pVlycfrXpzz6zT+eMMLiiG4EE6LXogVWjvcpWELQ+56V3D47bCD3c7m/kWHVQ4U4 o/TH5K4g9Tkeuo6b77EystaG3HhuEaENJrM= -----END CERTIFICATE----- galera-4-26.4.22/tests/conf/my.cnf.2.tmpl000644 000162 177776 00000000136 14755062442 021031 0ustar00jenkinsnogroup000000 000000 wsrep_sst_method=rsync wsrep_sst_auth=root:rootpass wsrep_sst_receive_address=127.0.1.1:10023 galera-4-26.4.22/tests/conf/galera-server-1.pem000644 000162 177776 00000012411 14755062442 022200 0ustar00jenkinsnogroup000000 000000 Certificate: Data: Version: 3 (0x2) Serial Number: d5:f7:c4:d3:19:e1:68:c4:c1:6b:4a:a5:43:62:4a:95 Signature Algorithm: sha256WithRSAEncryption Issuer: C=FI, ST=Uusimaa, L=Helsinki, O=Codership Oy, OU=Galera Devel, CN=Galera Int/emailAddress=devel@galeracluster.com Validity Not Before: Jan 21 10:30:06 2021 GMT Not After : Jan 6 10:30:06 2024 GMT Subject: C=FI, ST=Uusimaa, L=Helsinki, O=Codership Oy, OU=Galera Devel, CN=galera-server-1/emailAddress=devel@galeracluster.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:a6:e7:d0:7b:91:6a:79:c1:a8:61:2d:9e:9d:14: 68:8a:b0:2c:a0:3a:bc:8e:54:92:af:1a:43:27:a5: 93:a9:e5:4b:01:5c:f3:0f:18:72:d0:49:14:d2:bd: e7:c3:83:b8:07:73:53:b7:a6:ac:be:28:d1:1b:e1: c9:2b:9d:dc:14:a8:74:48:61:ba:8e:7a:ae:1e:33: ec:a8:c6:fd:84:1c:50:41:15:b9:f2:50:04:e7:3a: 82:9d:61:f8:f2:a7:2b:ae:ed:45:ae:0e:34:b0:bb: 2e:28:0d:3c:45:a7:2e:d6:6e:d6:8b:d7:36:62:45: 12:f5:cc:ae:06:df:15:75:64:bd:f7:d0:1d:dc:1a: 29:5f:4f:4c:9d:8c:11:0a:7c:43:c9:0d:ea:62:e3: 56:5b:63:ba:7c:b7:92:82:36:f0:2b:23:c1:50:1a: 0b:dc:75:92:1c:fb:0b:14:10:df:cf:5c:4d:d7:9c: 6c:b7:f3:6f:c5:9f:d6:a9:62:6d:03:86:58:2c:cc: 8a:93:0b:1a:91:67:81:bd:12:7e:56:a9:e5:e0:3f: a1:16:87:56:8e:12:e4:8f:35:34:c7:5e:1d:f4:a4: 5e:78:62:c3:65:6a:85:b1:ee:04:e9:a4:4a:8b:3e: 9c:8f:c0:72:79:03:03:4a:3e:4a:99:43:3c:49:d0: 61:e7 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Subject Key Identifier: 14:9A:3C:5D:69:13:D8:7B:E8:5E:92:2F:B0:45:32:47:52:F2:A4:85 X509v3 Authority Key Identifier: keyid:C7:1F:75:96:5D:5A:4A:CD:71:AB:39:78:3E:1D:80:2D:B4:13:C2:FF DirName:/C=FI/ST=Uusimaa/L=Helsinki/O=Codership Oy/OU=Galera Devel/CN=Galera Root CA/emailAddress=devel@galeracluster.com serial:92:CE:97:05:D3:DA:44:B3:CE:1E:D7:DA:F9:FE:E0:90 X509v3 Key Usage: Digital Signature, Key Encipherment X509v3 Subject Alternative Name: DNS:galera-server-1 Signature Algorithm: sha256WithRSAEncryption a4:ae:58:97:d3:06:7f:f8:56:52:6c:b0:56:f1:d7:b6:67:b8: 7a:1f:65:02:af:45:1c:fc:3b:5d:55:64:40:5b:5c:51:dd:3a: 26:64:4a:38:2c:fe:ab:74:12:b5:f2:0a:c3:e8:58:3f:16:15: b8:97:62:a5:34:d1:e8:4b:69:f8:7c:e5:af:f7:00:67:2f:43: 50:eb:94:58:7f:10:73:63:2a:b9:5c:1d:27:10:dd:98:e8:50: 53:24:5a:30:4e:ba:f4:db:33:25:9f:0f:35:92:90:d6:53:e1: 5b:74:cb:bd:13:0b:2e:4c:d1:bf:36:68:bb:70:ff:1e:d8:87: 9f:77:54:d0:d9:15:04:97:e5:97:25:92:ef:e6:ea:4b:12:74: 6e:a5:c9:7e:45:aa:f9:3e:e4:7e:65:a3:68:ee:d1:12:e7:33: 5e:67:cf:de:48:d0:c0:c0:d0:45:00:ef:a8:08:30:c6:ff:3e: 09:88:ad:79:9a:ec:35:e0:a5:bd:3d:37:9d:33:11:f0:5f:84: fb:e8:18:67:b5:cb:26:6f:25:c1:1b:2e:fd:c2:d2:98:d0:3c: 54:15:d1:1d:05:73:c5:2d:be:00:ef:cf:e7:53:0a:4f:b7:51: 26:f9:b8:31:50:56:f9:ea:f7:a5:3a:ad:ca:93:08:1d:93:2d: d3:29:ac:e8 -----BEGIN CERTIFICATE----- MIIFBjCCA+6gAwIBAgIRANX3xNMZ4WjEwWtKpUNiSpUwDQYJKoZIhvcNAQELBQAw gZ0xCzAJBgNVBAYTAkZJMRAwDgYDVQQIDAdVdXNpbWFhMREwDwYDVQQHDAhIZWxz aW5raTEVMBMGA1UECgwMQ29kZXJzaGlwIE95MRUwEwYDVQQLDAxHYWxlcmEgRGV2 ZWwxEzARBgNVBAMMCkdhbGVyYSBJbnQxJjAkBgkqhkiG9w0BCQEWF2RldmVsQGdh bGVyYWNsdXN0ZXIuY29tMB4XDTIxMDEyMTEwMzAwNloXDTI0MDEwNjEwMzAwNlow gaIxCzAJBgNVBAYTAkZJMRAwDgYDVQQIDAdVdXNpbWFhMREwDwYDVQQHDAhIZWxz aW5raTEVMBMGA1UECgwMQ29kZXJzaGlwIE95MRUwEwYDVQQLDAxHYWxlcmEgRGV2 ZWwxGDAWBgNVBAMMD2dhbGVyYS1zZXJ2ZXItMTEmMCQGCSqGSIb3DQEJARYXZGV2 ZWxAZ2FsZXJhY2x1c3Rlci5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK AoIBAQCm59B7kWp5wahhLZ6dFGiKsCygOryOVJKvGkMnpZOp5UsBXPMPGHLQSRTS vefDg7gHc1O3pqy+KNEb4ckrndwUqHRIYbqOeq4eM+yoxv2EHFBBFbnyUATnOoKd Yfjypyuu7UWuDjSwuy4oDTxFpy7WbtaL1zZiRRL1zK4G3xV1ZL330B3cGilfT0yd jBEKfEPJDepi41ZbY7p8t5KCNvArI8FQGgvcdZIc+wsUEN/PXE3XnGy382/Fn9ap Ym0DhlgszIqTCxqRZ4G9En5WqeXgP6EWh1aOEuSPNTTHXh30pF54YsNlaoWx7gTp pEqLPpyPwHJ5AwNKPkqZQzxJ0GHnAgMBAAGjggE4MIIBNDAJBgNVHRMEAjAAMB0G A1UdDgQWBBQUmjxdaRPYe+heki+wRTJHUvKkhTCB3gYDVR0jBIHWMIHTgBTHH3WW XVpKzXGrOXg+HYAttBPC/6GBp6SBpDCBoTELMAkGA1UEBhMCRkkxEDAOBgNVBAgM B1V1c2ltYWExETAPBgNVBAcMCEhlbHNpbmtpMRUwEwYDVQQKDAxDb2RlcnNoaXAg T3kxFTATBgNVBAsMDEdhbGVyYSBEZXZlbDEXMBUGA1UEAwwOR2FsZXJhIFJvb3Qg Q0ExJjAkBgkqhkiG9w0BCQEWF2RldmVsQGdhbGVyYWNsdXN0ZXIuY29tghEAks6X BdPaRLPOHtfa+f7gkDALBgNVHQ8EBAMCBaAwGgYDVR0RBBMwEYIPZ2FsZXJhLXNl cnZlci0xMA0GCSqGSIb3DQEBCwUAA4IBAQCkrliX0wZ/+FZSbLBW8de2Z7h6H2UC r0Uc/DtdVWRAW1xR3TomZEo4LP6rdBK18grD6Fg/FhW4l2KlNNHoS2n4fOWv9wBn L0NQ65RYfxBzYyq5XB0nEN2Y6FBTJFowTrr02zMlnw81kpDWU+FbdMu9EwsuTNG/ Nmi7cP8e2Iefd1TQ2RUEl+WXJZLv5upLEnRupcl+Rar5PuR+ZaNo7tES5zNeZ8/e SNDAwNBFAO+oCDDG/z4JiK15muw14KW9PTedMxHwX4T76BhntcsmbyXBGy79wtKY 0DxUFdEdBXPFLb4A78/nUwpPt1Em+bgxUFb56velOq3Kkwgdky3TKazo -----END CERTIFICATE----- galera-4-26.4.22/tests/conf/cluster.conf.tmpl000644 000162 177776 00000003164 14755062442 022110 0ustar00jenkinsnogroup000000 000000 # # Cluster resources configuration. # # DBMS that will be used for tests export DBMS=${DBMS:-"MYSQL"} # DBMS superuser username and password for administrative purposes export DBMS_ROOT_USER=${DBMS_ROOT_USER:-"root"} export DBMS_ROOT_PSWD=${DBMS_ROOT_PSWD:-"rootpass"} # DBMS test use username and password export DBMS_TEST_USER=${DBMS_TEST_USER:-"test"} export DBMS_TEST_PSWD=${DBMS_TEST_PSWD:-"testpass"} # DBMS schema to use for tests export DBMS_TEST_SCHEMA=${DBMS_TEST_SCHEMA:-"test"} # Host for clients to connect to export DBMS_HOST=${DBMS_HOST:-"127.0.1.1"} # Port for MySQL specific tests export MYSQL_PORT=${MYSQL_PORT:-"3306"} # Port for PostgreSQL specific tests export PGSQL_PORT=${PGSQL_PORT:-"5432"} # Port for crossplatform tests case "$DBMS" in "MYSQL") export DBMS_PORT=${DBMS_PORT:-"$MYSQL_PORT"} ;; "PGSQL") export DBMS_PORT=${DBMS_PORT:-"$PGSQL_PORT"} ;; esac # How many concurrent clients to use export DBMS_CLIENTS=${DBMS_CLIENTS:-"16"} # Type of GCS backend export GCS_TYPE=${GCS_TYPE:-"gcomm"} case "$GCS_TYPE" in "gcomm") ;; "vsbes") if [ -z "$VSBES_ADDRESS" ]; then echo "VSBES_ADDRESS is not set"; exit 1; fi ;; *) echo "Urecognized GCS_TYPE: '$GCS_TYPE'" ; exit 1 ;; esac # Define extra parameters passed to gcomm backend if needed, # e.g. using multicast: # # export GCOMM_EXTRA_PARAMS=${GCOMM_EXTRA_PARAMS:-"gmcast.mcast_addr=239.192.0.11"} export GCOMM_EXTRA_PARAMS="" # default replication port export GCS_PORT=4567 # common part of my.cnf export COMMON_MY_CNF=$BASE_CONF/common_my.cnf # libglb.so location if not standard (/usr/lib|/usr/local/lib) #GLB_LIB= . $BASE_CONF/nodes.conf # end galera-4-26.4.22/tests/conf/sqlgen.conf000644 000162 177776 00000000170 14755062442 020737 0ustar00jenkinsnogroup000000 000000 # Script test_sqlgen/run.sh --create 1 --rows 500 --duration 10 test_sqlgen/run.sh --create 1 --rows 1000 --duration 10 galera-4-26.4.22/tests/conf/README.md000644 000162 177776 00000001646 14755062442 020067 0ustar00jenkinsnogroup000000 000000 Certificates ============ Note: These certificates should be used for testing purposes only. Certificate and key files in this directory: Simple key and certificate -------------------------- This key and certificate must be identical on each node: - galera_key.pem/galera_cert.pem - standalone key/certificate which can be used for testing in symmetric setups Certificate Chain ----------------- Keys and certificates below have been created by using easy-rsa 3 CLI utility: https://github.com/OpenVPN/easy-rsa - galera-ca.pem - Root CA certificate for testing - galera-int.pem - Intermediate certificate for testing - galera-server-n.key Server private key for node n - galera-server-n.pem - Server certificate for node n - bundle-galera-server-n.pem File containing both server and intermediate certificate for node n See script gen-cert-chain.sh in this same directory for chain generation.galera-4-26.4.22/tests/conf/galera-server-3.pem000644 000162 177776 00000012411 14755062442 022202 0ustar00jenkinsnogroup000000 000000 Certificate: Data: Version: 3 (0x2) Serial Number: 2f:5c:d5:66:b6:b8:4f:56:0f:d9:f8:25:9b:b1:ae:5e Signature Algorithm: sha256WithRSAEncryption Issuer: C=FI, ST=Uusimaa, L=Helsinki, O=Codership Oy, OU=Galera Devel, CN=Galera Int/emailAddress=devel@galeracluster.com Validity Not Before: Jan 21 10:30:06 2021 GMT Not After : Jan 6 10:30:06 2024 GMT Subject: C=FI, ST=Uusimaa, L=Helsinki, O=Codership Oy, OU=Galera Devel, CN=galera-server-3/emailAddress=devel@galeracluster.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:b4:83:c2:ec:a8:91:e8:31:ab:07:05:97:95:c9: d6:82:a1:8e:47:96:43:83:fc:f8:c9:e4:73:e7:b8: 9d:61:22:20:44:65:b5:8f:59:c8:3d:b1:a5:84:f7: 7c:79:2b:23:b4:dc:aa:a0:b2:a2:2d:d9:a4:46:fd: 4c:28:61:83:b5:67:31:70:81:3f:15:a0:38:19:e8: 50:47:82:37:4f:06:d0:f2:db:57:ef:b1:b9:44:2f: dd:70:a8:b6:44:31:69:53:70:cd:76:24:ab:3a:35: be:68:23:8c:30:52:2a:d5:45:f2:16:5c:62:bd:ba: bc:f0:d8:52:b6:97:1f:38:99:c2:b1:d2:1c:bf:cf: 76:77:10:0c:f3:68:53:e8:a2:a8:9c:89:6b:d2:62: 7e:d6:d5:0a:73:d6:bf:f3:35:d9:20:9c:ec:5e:4d: c1:08:68:2f:9f:ac:e6:22:49:f8:31:99:78:1f:73: e1:92:13:a7:79:05:c1:b8:91:be:fb:25:a0:3d:ac: 4f:a8:33:bf:53:6e:5d:0d:83:e2:03:d9:03:d2:f2: df:b3:de:b1:2e:b5:a1:0e:25:52:15:ad:e1:9b:76: 46:7c:f9:c0:87:00:04:8c:a8:32:f1:39:5f:6e:f3: a1:05:36:6d:ab:82:21:0f:cd:18:37:ec:ad:f2:48: bc:c5 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Subject Key Identifier: 40:AA:07:8D:B7:E6:BA:A8:F1:46:89:E9:4F:5F:9D:8D:B1:40:97:1D X509v3 Authority Key Identifier: keyid:C7:1F:75:96:5D:5A:4A:CD:71:AB:39:78:3E:1D:80:2D:B4:13:C2:FF DirName:/C=FI/ST=Uusimaa/L=Helsinki/O=Codership Oy/OU=Galera Devel/CN=Galera Root CA/emailAddress=devel@galeracluster.com serial:92:CE:97:05:D3:DA:44:B3:CE:1E:D7:DA:F9:FE:E0:90 X509v3 Key Usage: Digital Signature, Key Encipherment X509v3 Subject Alternative Name: DNS:galera-server-3 Signature Algorithm: sha256WithRSAEncryption 1f:b7:e2:94:f0:23:0f:26:39:b6:e2:f4:68:ca:47:0d:1e:5c: 49:bb:19:14:d8:8b:3f:88:d9:05:c6:69:9a:bb:d4:61:ef:19: dd:e2:c8:68:26:77:0c:fc:7d:fc:00:75:de:01:b9:98:cd:10: 23:18:b9:e0:6b:0e:73:5d:63:3f:22:95:18:df:11:04:0d:3a: 43:4e:7e:3b:75:ee:9f:cd:1c:4d:05:92:c4:8b:3c:02:22:f5: ec:1a:50:32:ff:a1:5b:cf:8d:07:4a:a4:97:a1:bf:6a:5c:b1: 24:ef:2b:d6:0d:7c:70:1d:58:35:8b:da:e3:37:be:cc:94:cb: 2a:05:aa:32:de:a6:00:9c:c4:f6:cb:ec:0a:fe:cc:2f:dc:e7: 76:7f:61:30:23:ae:fe:ff:f6:57:34:9c:3d:8f:ff:b3:0a:6f: 7b:f9:fc:e2:5b:aa:17:a2:8d:10:5f:46:ee:23:98:a9:06:b9: 2c:11:52:fe:14:ed:02:4c:85:af:06:d0:f7:bb:f8:aa:d3:9b: 0f:5c:5c:10:df:7c:a8:ee:6a:c7:b9:67:e3:9e:fe:cc:24:66: 08:3b:6f:80:53:1d:99:e5:94:a0:d9:bc:27:ab:1a:26:c7:04: 0e:d2:3e:5c:5d:ac:7d:06:1a:de:d5:28:bd:b5:96:23:59:e2: 9b:2a:cb:0a -----BEGIN CERTIFICATE----- MIIFBTCCA+2gAwIBAgIQL1zVZra4T1YP2fglm7GuXjANBgkqhkiG9w0BAQsFADCB nTELMAkGA1UEBhMCRkkxEDAOBgNVBAgMB1V1c2ltYWExETAPBgNVBAcMCEhlbHNp bmtpMRUwEwYDVQQKDAxDb2RlcnNoaXAgT3kxFTATBgNVBAsMDEdhbGVyYSBEZXZl bDETMBEGA1UEAwwKR2FsZXJhIEludDEmMCQGCSqGSIb3DQEJARYXZGV2ZWxAZ2Fs ZXJhY2x1c3Rlci5jb20wHhcNMjEwMTIxMTAzMDA2WhcNMjQwMTA2MTAzMDA2WjCB ojELMAkGA1UEBhMCRkkxEDAOBgNVBAgMB1V1c2ltYWExETAPBgNVBAcMCEhlbHNp bmtpMRUwEwYDVQQKDAxDb2RlcnNoaXAgT3kxFTATBgNVBAsMDEdhbGVyYSBEZXZl bDEYMBYGA1UEAwwPZ2FsZXJhLXNlcnZlci0zMSYwJAYJKoZIhvcNAQkBFhdkZXZl bEBnYWxlcmFjbHVzdGVyLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBALSDwuyokegxqwcFl5XJ1oKhjkeWQ4P8+Mnkc+e4nWEiIERltY9ZyD2xpYT3 fHkrI7TcqqCyoi3ZpEb9TChhg7VnMXCBPxWgOBnoUEeCN08G0PLbV++xuUQv3XCo tkQxaVNwzXYkqzo1vmgjjDBSKtVF8hZcYr26vPDYUraXHziZwrHSHL/PdncQDPNo U+iiqJyJa9JiftbVCnPWv/M12SCc7F5NwQhoL5+s5iJJ+DGZeB9z4ZITp3kFwbiR vvsloD2sT6gzv1NuXQ2D4gPZA9Ly37PesS61oQ4lUhWt4Zt2Rnz5wIcABIyoMvE5 X27zoQU2bauCIQ/NGDfsrfJIvMUCAwEAAaOCATgwggE0MAkGA1UdEwQCMAAwHQYD VR0OBBYEFECqB4235rqo8UaJ6U9fnY2xQJcdMIHeBgNVHSMEgdYwgdOAFMcfdZZd WkrNcas5eD4dgC20E8L/oYGnpIGkMIGhMQswCQYDVQQGEwJGSTEQMA4GA1UECAwH VXVzaW1hYTERMA8GA1UEBwwISGVsc2lua2kxFTATBgNVBAoMDENvZGVyc2hpcCBP eTEVMBMGA1UECwwMR2FsZXJhIERldmVsMRcwFQYDVQQDDA5HYWxlcmEgUm9vdCBD QTEmMCQGCSqGSIb3DQEJARYXZGV2ZWxAZ2FsZXJhY2x1c3Rlci5jb22CEQCSzpcF 09pEs84e19r5/uCQMAsGA1UdDwQEAwIFoDAaBgNVHREEEzARgg9nYWxlcmEtc2Vy dmVyLTMwDQYJKoZIhvcNAQELBQADggEBAB+34pTwIw8mObbi9GjKRw0eXEm7GRTY iz+I2QXGaZq71GHvGd3iyGgmdwz8ffwAdd4BuZjNECMYueBrDnNdYz8ilRjfEQQN OkNOfjt17p/NHE0FksSLPAIi9ewaUDL/oVvPjQdKpJehv2pcsSTvK9YNfHAdWDWL 2uM3vsyUyyoFqjLepgCcxPbL7Ar+zC/c53Z/YTAjrv7/9lc0nD2P/7MKb3v5/OJb qheijRBfRu4jmKkGuSwRUv4U7QJMha8G0Pe7+KrTmw9cXBDffKjuase5Z+Oe/swk Zgg7b4BTHZnllKDZvCerGibHBA7SPlxdrH0GGt7VKL21liNZ4psqywo= -----END CERTIFICATE----- galera-4-26.4.22/tests/conf/galera_ca.pem000644 000162 177776 00000002436 14755062442 021207 0ustar00jenkinsnogroup000000 000000 -----BEGIN CERTIFICATE----- MIIDmzCCAoOgAwIBAgIUThWEH6e2r32TxCzqAFw7qlTe4IUwDQYJKoZIhvcNAQEL BQAwXTELMAkGA1UEBhMCRkkxETAPBgNVBAgMCEhlbHNpbmtpMREwDwYDVQQHDAhI ZWxzaW5raTESMBAGA1UECgwJQ29kZXJzaGlwMRQwEgYDVQQDDAtHYWxlcmEgUm9v dDAeFw0yMTAyMjgxOTQ4NTNaFw0yMzExMjUxOTQ4NTNaMF0xCzAJBgNVBAYTAkZJ MREwDwYDVQQIDAhIZWxzaW5raTERMA8GA1UEBwwISGVsc2lua2kxEjAQBgNVBAoM CUNvZGVyc2hpcDEUMBIGA1UEAwwLR2FsZXJhIFJvb3QwggEiMA0GCSqGSIb3DQEB AQUAA4IBDwAwggEKAoIBAQCqPInsAAcnnYLQXXUfqSyq/TVm90ZqLiHYDhzArwg/ Kq6L54ulD6zoFWp5jkTzA/DdZRkxeY7rYp8xPuI8PFCaXr6+HYV7Ft7SPZbdtBYL dimhH0XqQkFgR1jysSbRzlcUOSBekJFD5DzYMoK1rpHPmta6ZF7+QRy206/sNf4P vRAhJXeFIe8qUnuZEvVFAAjonVfHyR0YNFtvRrpHlqH9jBLN47qFdlQWSck3v3O1 wmjVNvPLkNLXfCf62+uzlXaztxKaLQiisO5ur4k1ryvWmZfttGVtORtsGDnU2Fzw dqdxKnKRoxoUFAlmQWnGFjigJiWbFwN7fvC7ARrxQ83JAgMBAAGjUzBRMB0GA1Ud DgQWBBTH4oT4mC2UZSn7U4EtroBfTvZbujAfBgNVHSMEGDAWgBTH4oT4mC2UZSn7 U4EtroBfTvZbujAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAR bid7qDGJ4HMlnHGmh8YdgYJrB6ChYvuVvcDSMMomHhlWuJUXplbjVBwi13cDQhOk IVGE7+zCW/wRxUyoA+2Z/YDJ5ekGdwav4zTOQ8XGD0aASxdkrfJLQZ+DGTqv43gL nRe7/MOJTtzCybRJRN8NMQysfukVr5qENoxnMGS40knJ/mV9mkb/du9nXi1OH21t y6kPjVC6axWOrbbaObC/d26QfD3V+t8sPu0q3PrAYhmgVYNUCuUV94iwlpDsn/9A TYQWE6v1JGDavX5T9S19nw6tsp2gmHy1bjDyTMfDDPnZ+1QQ7m0BbuKghnyh6qTL tIKBtDgvcmxl8bdsaTgB -----END CERTIFICATE----- galera-4-26.4.22/tests/conf/nodes.conf.tmpl000644 000162 177776 00000003576 14755062442 021546 0ustar00jenkinsnogroup000000 000000 # # Nodes specific configuration # # Symbolic node ID - for output, for future use shoudl be unique declare -a NODE_ID # Location of the node. "local" - means this machine, anything else # is interpreted as SSH arguments declare -a NODE_LOCATION # Absolute path to where to unpack the distribution declare -a NODE_TEST_DIR # Address for incoming requests declare -a NODE_INCOMING_HOST declare -a NODE_INCOMING_PORT # Address for group communicaiton declare -a NODE_GCS_HOST declare -a NODE_GCS_PORT # Optional server configuration file to be copied to node declare -a NODE_MY_CNF declare -a NODE_PG_CNF # 1st node idx=0 NODE_ID[$idx]="home" NODE_INCOMING_HOST[$idx]=127.0.0.1 NODE_INCOMING_PORT[$idx]=$(( $DBMS_PORT - 1 )) NODE_GCS_HOST[$idx]=192.168.0.1 NODE_GCS_PORT[$idx]=$GCS_PORT NODE_LOCATION[$idx]="local" NODE_TEST_DIR[$idx]=/tmp/galera NODE_MY_CNF[$idx]="$TEST_BASE/conf/my.cnf" # 2nd node idx=$(( $idx + 1 )) NODE_ID[$idx]="bernhard" NODE_INCOMING_HOST[$idx]=192.168.0.3 NODE_INCOMING_PORT[$idx]=$DBMS_PORT NODE_GCS_HOST[$idx]=${NODE_INCOMING_HOST[$idx]} NODE_GCS_PORT[$idx]=$GCS_PORT NODE_LOCATION[$idx]="alex@${NODE_INCOMING_HOST[$idx]}" NODE_TEST_DIR[$idx]=/home/alex/codership/galera NODE_MY_CNF[$idx]="$TEST_BASE/conf/my.cnf" # 3rd node #idx=$(( $idx + 1 )) #NODE_ID[$idx]="bulldog" #NODE_INCOMING_HOST[$idx]=192.168.0.12 #NODE_INCOMING_PORT[$idx]=$DBMS_PORT #NODE_GCS_HOST[$idx]=${NODE_INCOMING_HOST[$idx]} #NODE_GCS_PORT[$idx]=$GCS_PORT #NODE_LOCATION[$idx]="alex@${NODE_INCOMING_HOST[$idx]}" #NODE_TEST_DIR[$idx]=/home/alex/codership/galera #NODE_MY_CNF[$idx]="$TEST_BASE/conf/my.cnf" declare -xr NODE_MAX=$idx declare -xr NODE_LIST=$(seq 0 $NODE_MAX) declare -xr NODE_ID declare -xr NODE_LOCATION declare -xr NODE_TEST_DIR declare -xr NODE_INCOMING_HOST declare -xr NODE_INCOMING_PORT declare -xr NODE_GCS_HOST declare -xr NODE_GCS_PORT declare -xr NODE_MY_CNF declare -xr NODE_PG_CNF # galera-4-26.4.22/tests/conf/dummy.conf000644 000162 177776 00000000077 14755062442 020607 0ustar00jenkinsnogroup000000 000000 # Script arguments t/dummy.sh arg1 arg2 t/dummy.sh arg3 arg4 galera-4-26.4.22/tests/conf/bundle-galera-server-1.pem000644 000162 177776 00000006766 14755062442 023467 0ustar00jenkinsnogroup000000 000000 -----BEGIN CERTIFICATE----- MIIFBjCCA+6gAwIBAgIRANX3xNMZ4WjEwWtKpUNiSpUwDQYJKoZIhvcNAQELBQAw gZ0xCzAJBgNVBAYTAkZJMRAwDgYDVQQIDAdVdXNpbWFhMREwDwYDVQQHDAhIZWxz aW5raTEVMBMGA1UECgwMQ29kZXJzaGlwIE95MRUwEwYDVQQLDAxHYWxlcmEgRGV2 ZWwxEzARBgNVBAMMCkdhbGVyYSBJbnQxJjAkBgkqhkiG9w0BCQEWF2RldmVsQGdh bGVyYWNsdXN0ZXIuY29tMB4XDTIxMDEyMTEwMzAwNloXDTI0MDEwNjEwMzAwNlow gaIxCzAJBgNVBAYTAkZJMRAwDgYDVQQIDAdVdXNpbWFhMREwDwYDVQQHDAhIZWxz aW5raTEVMBMGA1UECgwMQ29kZXJzaGlwIE95MRUwEwYDVQQLDAxHYWxlcmEgRGV2 ZWwxGDAWBgNVBAMMD2dhbGVyYS1zZXJ2ZXItMTEmMCQGCSqGSIb3DQEJARYXZGV2 ZWxAZ2FsZXJhY2x1c3Rlci5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK AoIBAQCm59B7kWp5wahhLZ6dFGiKsCygOryOVJKvGkMnpZOp5UsBXPMPGHLQSRTS vefDg7gHc1O3pqy+KNEb4ckrndwUqHRIYbqOeq4eM+yoxv2EHFBBFbnyUATnOoKd Yfjypyuu7UWuDjSwuy4oDTxFpy7WbtaL1zZiRRL1zK4G3xV1ZL330B3cGilfT0yd jBEKfEPJDepi41ZbY7p8t5KCNvArI8FQGgvcdZIc+wsUEN/PXE3XnGy382/Fn9ap Ym0DhlgszIqTCxqRZ4G9En5WqeXgP6EWh1aOEuSPNTTHXh30pF54YsNlaoWx7gTp pEqLPpyPwHJ5AwNKPkqZQzxJ0GHnAgMBAAGjggE4MIIBNDAJBgNVHRMEAjAAMB0G A1UdDgQWBBQUmjxdaRPYe+heki+wRTJHUvKkhTCB3gYDVR0jBIHWMIHTgBTHH3WW XVpKzXGrOXg+HYAttBPC/6GBp6SBpDCBoTELMAkGA1UEBhMCRkkxEDAOBgNVBAgM B1V1c2ltYWExETAPBgNVBAcMCEhlbHNpbmtpMRUwEwYDVQQKDAxDb2RlcnNoaXAg T3kxFTATBgNVBAsMDEdhbGVyYSBEZXZlbDEXMBUGA1UEAwwOR2FsZXJhIFJvb3Qg Q0ExJjAkBgkqhkiG9w0BCQEWF2RldmVsQGdhbGVyYWNsdXN0ZXIuY29tghEAks6X BdPaRLPOHtfa+f7gkDALBgNVHQ8EBAMCBaAwGgYDVR0RBBMwEYIPZ2FsZXJhLXNl cnZlci0xMA0GCSqGSIb3DQEBCwUAA4IBAQCkrliX0wZ/+FZSbLBW8de2Z7h6H2UC r0Uc/DtdVWRAW1xR3TomZEo4LP6rdBK18grD6Fg/FhW4l2KlNNHoS2n4fOWv9wBn L0NQ65RYfxBzYyq5XB0nEN2Y6FBTJFowTrr02zMlnw81kpDWU+FbdMu9EwsuTNG/ Nmi7cP8e2Iefd1TQ2RUEl+WXJZLv5upLEnRupcl+Rar5PuR+ZaNo7tES5zNeZ8/e SNDAwNBFAO+oCDDG/z4JiK15muw14KW9PTedMxHwX4T76BhntcsmbyXBGy79wtKY 0DxUFdEdBXPFLb4A78/nUwpPt1Em+bgxUFb56velOq3Kkwgdky3TKazo -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIE7zCCA9egAwIBAgIRAJLOlwXT2kSzzh7X2vn+4JAwDQYJKoZIhvcNAQELBQAw gaExCzAJBgNVBAYTAkZJMRAwDgYDVQQIDAdVdXNpbWFhMREwDwYDVQQHDAhIZWxz aW5raTEVMBMGA1UECgwMQ29kZXJzaGlwIE95MRUwEwYDVQQLDAxHYWxlcmEgRGV2 ZWwxFzAVBgNVBAMMDkdhbGVyYSBSb290IENBMSYwJAYJKoZIhvcNAQkBFhdkZXZl bEBnYWxlcmFjbHVzdGVyLmNvbTAeFw0yMTAxMjExMDMwMDZaFw0yNDAxMDYxMDMw MDZaMIGdMQswCQYDVQQGEwJGSTEQMA4GA1UECAwHVXVzaW1hYTERMA8GA1UEBwwI SGVsc2lua2kxFTATBgNVBAoMDENvZGVyc2hpcCBPeTEVMBMGA1UECwwMR2FsZXJh IERldmVsMRMwEQYDVQQDDApHYWxlcmEgSW50MSYwJAYJKoZIhvcNAQkBFhdkZXZl bEBnYWxlcmFjbHVzdGVyLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBAM67vDBE4ALnYbfDKtAphdd/rFSmtiaOmZcl58RAPpaaQQ/jxLwNvVgsaCh/ 2boWBXhGNCzB175sxcK2XkKEn9MHENARKDzfdFoIQjPqQyZxX6CupYOdnA/B9+l/ +6uW7Iu+N6UP+IUeW/ElWzbIh5k/mpzOr16r/MCmxD9dvB7i1C5+bvXB6lasN2ot tldw6cQtrzGBmYRl3f/hUq3j9gwrPm3SVfuoEPCoUetobgUZbKdi0jHuYRi4y60T +Vn7Xx1fcPz9qwyWJHtw9ERr/6KWhxOk9+rxiUmNIah337p+nOe282lCmnI+hMLo CSeyjlPVzIxQY5KjTFW4zu6wTWkCAwEAAaOCASIwggEeMAwGA1UdEwQFMAMBAf8w HQYDVR0OBBYEFMcfdZZdWkrNcas5eD4dgC20E8L/MIHhBgNVHSMEgdkwgdaAFMQb 2PuGq8uVODtrOl5vGvhP/0rwoYGnpIGkMIGhMQswCQYDVQQGEwJGSTEQMA4GA1UE CAwHVXVzaW1hYTERMA8GA1UEBwwISGVsc2lua2kxFTATBgNVBAoMDENvZGVyc2hp cCBPeTEVMBMGA1UECwwMR2FsZXJhIERldmVsMRcwFQYDVQQDDA5HYWxlcmEgUm9v dCBDQTEmMCQGCSqGSIb3DQEJARYXZGV2ZWxAZ2FsZXJhY2x1c3Rlci5jb22CFHkh Yqjh/DAHX8QPlm+UBTerUrwzMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOC AQEAaJuugEguz+T4V8umj0zAv/yfWOw+647scCJpfF1P2q2GSTDGG3LkjrDuKMK+ zp0hAHdXhDD8XDOh2q6eMbUTCVUFqnM6ss4os/HyK5f5UCv3gJaSXJm3GAGgmZze HsdDPRTePp1Mr21EjChsWEUAb6EKA5F6ezUVglFwi4uD00FNYA/If7mbizomohS9 JvXPhriy9cB1jaLgP5UOl1tT3CJUjNY2Jdk3RKOIEthUaHxY7xnJVLGrLQrMBjG7 dKPuxucxvpKBAMhqnLdNVBYg3wb7WAzAMVedUrJWpXaWzWIsjLtadt6M3kCwYKtS h3vB5D/rqXSP0pdm50ihb3yRHA== -----END CERTIFICATE----- galera-4-26.4.22/tests/conf/common_my.cnf.tmpl000644 000162 177776 00000001236 14755062442 022243 0ustar00jenkinsnogroup000000 000000 [mysqld] core-file bind-address=127.0.1.1 binlog_cache_size=4096 innodb_flush_log_at_trx_commit=0 innodb_buffer_pool_size=64M innodb_log_file_size=64M innodb_locks_unsafe_for_binlog=1 innodb_lock_wait_timeout=50 innodb_autoinc_lock_mode=2 # query_cache_size=1M query_cache_type=1 max_connections=1024 max_connect_errors=4294967295 performance_schema=OFF binlog_format=ROW default-storage-engine=innodb wsrep_slave_threads=4 # Some regression tests use causal reads to determine if # all pending queries have been processed. Set to sufficiently high # time to avoid timing out too early. wsrep_provider_options="repl.causal_read_timeout=PT10H;evs.info_log_mask=0x3" galera-4-26.4.22/tests/conf/galera-int.pem000644 000162 177776 00000012234 14755062442 021331 0ustar00jenkinsnogroup000000 000000 Certificate: Data: Version: 3 (0x2) Serial Number: 92:ce:97:05:d3:da:44:b3:ce:1e:d7:da:f9:fe:e0:90 Signature Algorithm: sha256WithRSAEncryption Issuer: C=FI, ST=Uusimaa, L=Helsinki, O=Codership Oy, OU=Galera Devel, CN=Galera Root CA/emailAddress=devel@galeracluster.com Validity Not Before: Jan 21 10:30:06 2021 GMT Not After : Jan 6 10:30:06 2024 GMT Subject: C=FI, ST=Uusimaa, L=Helsinki, O=Codership Oy, OU=Galera Devel, CN=Galera Int/emailAddress=devel@galeracluster.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:ce:bb:bc:30:44:e0:02:e7:61:b7:c3:2a:d0:29: 85:d7:7f:ac:54:a6:b6:26:8e:99:97:25:e7:c4:40: 3e:96:9a:41:0f:e3:c4:bc:0d:bd:58:2c:68:28:7f: d9:ba:16:05:78:46:34:2c:c1:d7:be:6c:c5:c2:b6: 5e:42:84:9f:d3:07:10:d0:11:28:3c:df:74:5a:08: 42:33:ea:43:26:71:5f:a0:ae:a5:83:9d:9c:0f:c1: f7:e9:7f:fb:ab:96:ec:8b:be:37:a5:0f:f8:85:1e: 5b:f1:25:5b:36:c8:87:99:3f:9a:9c:ce:af:5e:ab: fc:c0:a6:c4:3f:5d:bc:1e:e2:d4:2e:7e:6e:f5:c1: ea:56:ac:37:6a:2d:b6:57:70:e9:c4:2d:af:31:81: 99:84:65:dd:ff:e1:52:ad:e3:f6:0c:2b:3e:6d:d2: 55:fb:a8:10:f0:a8:51:eb:68:6e:05:19:6c:a7:62: d2:31:ee:61:18:b8:cb:ad:13:f9:59:fb:5f:1d:5f: 70:fc:fd:ab:0c:96:24:7b:70:f4:44:6b:ff:a2:96: 87:13:a4:f7:ea:f1:89:49:8d:21:a8:77:df:ba:7e: 9c:e7:b6:f3:69:42:9a:72:3e:84:c2:e8:09:27:b2: 8e:53:d5:cc:8c:50:63:92:a3:4c:55:b8:ce:ee:b0: 4d:69 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:TRUE X509v3 Subject Key Identifier: C7:1F:75:96:5D:5A:4A:CD:71:AB:39:78:3E:1D:80:2D:B4:13:C2:FF X509v3 Authority Key Identifier: keyid:C4:1B:D8:FB:86:AB:CB:95:38:3B:6B:3A:5E:6F:1A:F8:4F:FF:4A:F0 DirName:/C=FI/ST=Uusimaa/L=Helsinki/O=Codership Oy/OU=Galera Devel/CN=Galera Root CA/emailAddress=devel@galeracluster.com serial:79:21:62:A8:E1:FC:30:07:5F:C4:0F:96:6F:94:05:37:AB:52:BC:33 X509v3 Key Usage: Certificate Sign, CRL Sign Signature Algorithm: sha256WithRSAEncryption 68:9b:ae:80:48:2e:cf:e4:f8:57:cb:a6:8f:4c:c0:bf:fc:9f: 58:ec:3e:eb:8e:ec:70:22:69:7c:5d:4f:da:ad:86:49:30:c6: 1b:72:e4:8e:b0:ee:28:c2:be:ce:9d:21:00:77:57:84:30:fc: 5c:33:a1:da:ae:9e:31:b5:13:09:55:05:aa:73:3a:b2:ce:28: b3:f1:f2:2b:97:f9:50:2b:f7:80:96:92:5c:99:b7:18:01:a0: 99:9c:de:1e:c7:43:3d:14:de:3e:9d:4c:af:6d:44:8c:28:6c: 58:45:00:6f:a1:0a:03:91:7a:7b:35:15:82:51:70:8b:8b:83: d3:41:4d:60:0f:c8:7f:b9:9b:8b:3a:26:a2:14:bd:26:f5:cf: 86:b8:b2:f5:c0:75:8d:a2:e0:3f:95:0e:97:5b:53:dc:22:54: 8c:d6:36:25:d9:37:44:a3:88:12:d8:54:68:7c:58:ef:19:c9: 54:b1:ab:2d:0a:cc:06:31:bb:74:a3:ee:c6:e7:31:be:92:81: 00:c8:6a:9c:b7:4d:54:16:20:df:06:fb:58:0c:c0:31:57:9d: 52:b2:56:a5:76:96:cd:62:2c:8c:bb:5a:76:de:8c:de:40:b0: 60:ab:52:87:7b:c1:e4:3f:eb:a9:74:8f:d2:97:66:e7:48:a1: 6f:7c:91:1c -----BEGIN CERTIFICATE----- MIIE7zCCA9egAwIBAgIRAJLOlwXT2kSzzh7X2vn+4JAwDQYJKoZIhvcNAQELBQAw gaExCzAJBgNVBAYTAkZJMRAwDgYDVQQIDAdVdXNpbWFhMREwDwYDVQQHDAhIZWxz aW5raTEVMBMGA1UECgwMQ29kZXJzaGlwIE95MRUwEwYDVQQLDAxHYWxlcmEgRGV2 ZWwxFzAVBgNVBAMMDkdhbGVyYSBSb290IENBMSYwJAYJKoZIhvcNAQkBFhdkZXZl bEBnYWxlcmFjbHVzdGVyLmNvbTAeFw0yMTAxMjExMDMwMDZaFw0yNDAxMDYxMDMw MDZaMIGdMQswCQYDVQQGEwJGSTEQMA4GA1UECAwHVXVzaW1hYTERMA8GA1UEBwwI SGVsc2lua2kxFTATBgNVBAoMDENvZGVyc2hpcCBPeTEVMBMGA1UECwwMR2FsZXJh IERldmVsMRMwEQYDVQQDDApHYWxlcmEgSW50MSYwJAYJKoZIhvcNAQkBFhdkZXZl bEBnYWxlcmFjbHVzdGVyLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBAM67vDBE4ALnYbfDKtAphdd/rFSmtiaOmZcl58RAPpaaQQ/jxLwNvVgsaCh/ 2boWBXhGNCzB175sxcK2XkKEn9MHENARKDzfdFoIQjPqQyZxX6CupYOdnA/B9+l/ +6uW7Iu+N6UP+IUeW/ElWzbIh5k/mpzOr16r/MCmxD9dvB7i1C5+bvXB6lasN2ot tldw6cQtrzGBmYRl3f/hUq3j9gwrPm3SVfuoEPCoUetobgUZbKdi0jHuYRi4y60T +Vn7Xx1fcPz9qwyWJHtw9ERr/6KWhxOk9+rxiUmNIah337p+nOe282lCmnI+hMLo CSeyjlPVzIxQY5KjTFW4zu6wTWkCAwEAAaOCASIwggEeMAwGA1UdEwQFMAMBAf8w HQYDVR0OBBYEFMcfdZZdWkrNcas5eD4dgC20E8L/MIHhBgNVHSMEgdkwgdaAFMQb 2PuGq8uVODtrOl5vGvhP/0rwoYGnpIGkMIGhMQswCQYDVQQGEwJGSTEQMA4GA1UE CAwHVXVzaW1hYTERMA8GA1UEBwwISGVsc2lua2kxFTATBgNVBAoMDENvZGVyc2hp cCBPeTEVMBMGA1UECwwMR2FsZXJhIERldmVsMRcwFQYDVQQDDA5HYWxlcmEgUm9v dCBDQTEmMCQGCSqGSIb3DQEJARYXZGV2ZWxAZ2FsZXJhY2x1c3Rlci5jb22CFHkh Yqjh/DAHX8QPlm+UBTerUrwzMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOC AQEAaJuugEguz+T4V8umj0zAv/yfWOw+647scCJpfF1P2q2GSTDGG3LkjrDuKMK+ zp0hAHdXhDD8XDOh2q6eMbUTCVUFqnM6ss4os/HyK5f5UCv3gJaSXJm3GAGgmZze HsdDPRTePp1Mr21EjChsWEUAb6EKA5F6ezUVglFwi4uD00FNYA/If7mbizomohS9 JvXPhriy9cB1jaLgP5UOl1tT3CJUjNY2Jdk3RKOIEthUaHxY7xnJVLGrLQrMBjG7 dKPuxucxvpKBAMhqnLdNVBYg3wb7WAzAMVedUrJWpXaWzWIsjLtadt6M3kCwYKtS h3vB5D/rqXSP0pdm50ihb3yRHA== -----END CERTIFICATE----- galera-4-26.4.22/tests/conf/galera_key.pem000644 000162 177776 00000003250 14755062442 021407 0ustar00jenkinsnogroup000000 000000 -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHrDam1TsHQPm+ E7tZcI8dGpa0wTooKh3WWPRIjcpOuDXlV8cxyK3YZAmquAT4iWcJQZZq8t+/LrpG K5i9sQl8dAjLftJKIRquEdwO027xF0hsqPiDMvfbBoy8af78tf3d3AFvq+SwL5jL nTOYkAqEYXbK6m64/mAN0npyrqX9ULsPQkQggnbZYYJfRa3V3I2QfLMlFD7Y8oy5 fZTKOUOf8F1neCGNlMtGXWl3I3CW13uVyS2JmZSmI+iPtxmFOunLK+ymfmmH+6/v 2EGOPZ2JNbQ1jLFPlLMfgKamSPzkI0U0jAsomFtiJqUceJFdl+WAQXKc7OUWyd5r GYvRJmGdAgMBAAECggEAJxrIHUZXaCVGg/5TS8HqlVWWvFG3dRRCga8sMGVsgJw9 Gexl+48E1QNKjV+6wX4PatWogsuMHGInYwS4xjW82N3Utx3gS1pOYlbnFRQ9fwQD UByLhw5dUkznFSNhJ9bwoBmtQlSU1hiMQRxJdZCILsLzw+vDwYE4CJz3FCy2H3Tt 9jVrt0hkWBpm6mE188U/0439vxbOz8aIQmH0maaGYMXNi+2cGmTHwIZr89c+MLJN yH7wG1cveCCMbQtesgkl8F9+GqTJlByt3DWCupQ6jrcvEGRaybD8ELrVxPZkFckQ UXOBrZzS/Jx4bmK/4Vo/ljl5qaMLnrY/2WkJgpzPFQKBgQD95aM2NBU5iI+Ls4jo moKUK2eErZWXwh2sCxlByXtvuEL0fDrtjsbwtn9usmtjHvRHz9rYeXhp4kkteUOe DptTvQrBlbPuLZMQ5j/46fw+cCF6NgM8IhwHwhy2xifk7eHbjxBPQtDOeNZPekIV kOwLYrFP1/xZrPOFPvTanlt62wKBgQDJU5lPxPjx6XfFEiKGiytPUuVn98r3TFaf SCCR4PxgS4+GcYx3Oz2+sSwtGwThxN38pd0/N5iGRML2+Nsp+mLfZ3oEB5QL1xAL Q4duW1DOeXsHpPTM9OkbWCWOvsHffikyYyDXBWIOVCwnEFkahgCAc4G8yTGfGrx3 xosdHD9y5wKBgQD1TOZROiS/f1bgrEa8JHvAV+cd4u/CvYMZc2eljdo6aBYYS+ZL GkTO0CNyeeMS0xdEQme3+jQCaOQ2kRBpJsefMeewfMhod5O9Ihfwj3Brir5tar3Z XUMjo8FGPVDR95rdG+2wBmfi9BBqnT43w9qqbWHOOGjQ4y4sMFU02wabPQKBgGCk xN/KCkb2cAwmHHTBsdSuUnmKNeBowNxNX3Unr376RrefInLJ+WXk3vP4GGvYeUei x4ZlRc6Oi5jK9Uo9a+EaZzQv/x1/66+8hvKu2yeCoVCWGv2YQ55gvga8A40pntUV SNpvNxbxyRAnhN56nRsMV6csXammx/onUPh0avDZAoGBAJ+fM93yHuZ5zhJ17qN2 OsquEIq/4V8ChV77vLaWxlWIi48LYbMCDQMUxz+fcXw6v9nkFIDt0jEnn0cf54hf /oZNgQBKRgwvPbXVzBYt/9hdxZY0nCc54GnZe+dVx7cbC6PPQ/HB55lgFqfBbW5A 7N+ImmmIeBWkr5aXGKA+fvnj -----END PRIVATE KEY----- galera-4-26.4.22/tests/conf/gen-cert-chain.sh000755 000162 177776 00000004360 14755062442 021727 0ustar00jenkinsnogroup000000 000000 #!/usr/bin/env bash # # Copyright (C) 2021 Codership Oy # # Helper script to generate certificate chains for testing. # set -o errexit -o errtrace -o nounset set -o xtrace export EASYRSA_REQ_ORG="Codership Oy" export EASYRSA_REQ_EMAIL="devel@galeracluster.com" export EASYRSA_REQ_OU="Galera Devel" export EASYRSA_REQ_CITY="Helsinki" export EASYRSA_REQ_PROVINCE="Uusimaa" export EASYRSA_REQ_COUNTRY="FI" export EASYRSA_DN=org export EASYRSA_BATCH=yes # Init pki directory for root CA easyrsa --pki-dir=rootCA init-pki # Create root CA in pki/ca.crt easyrsa --pki-dir=rootCA --req-cn="Galera Root CA" build-ca nopass # Initialize pki directory for intermediate CA easyrsa --pki-dir=intCA init-pki easyrsa --pki-dir=intCA build-ca nopass subca # Create request for intermediate CA easyrsa --pki-dir=intCA --req-cn="Galera Int" gen-req ca nopass mkdir -p intCA/x509-types # Write custom server type for server certificates without # extendedKeyUsage. cat < intCA/x509-types/server basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always # extendedKeyUsage = serverAuth keyUsage = digitalSignature,keyEncipherment EOF # Copy request under rootCA cp intCA/reqs/ca.req rootCA/reqs/galera-int.req # Create intermediate CA in pki/issued/galear-int.crt easyrsa --pki-dir=rootCA sign-req ca galera-int # Copy generated intermediate CA under intCA cp rootCA/issued/galera-int.crt intCA/ca.crt # Create server certificates using intermediate CA for i in galera-server-1 galera-server-2 galera-server-3 do easyrsa --pki-dir=intCA --req-cn=$i gen-req $i nopass easyrsa --pki-dir=intCA sign-req server $i done cp rootCA/ca.crt galera-ca.pem cp intCA/ca.crt galera-int.pem find intCA/issued -name "*.crt" -exec sh -c 'x="{}"; cp "$x" "$(basename $x .crt).pem"' \; find intCA/private -name "*.key" -exec cp {} . \; # Validate generated certificates openssl verify -CAfile galera-ca.pem galera-int.pem for i in galera-server-1.pem galera-server-2.pem galera-server-3.pem do openssl verify -CAfile galera-ca.pem -untrusted galera-int.pem $i openssl x509 -in $i > bundle-$i openssl x509 -in galera-int.pem >> bundle-$i openssl crl2pkcs7 -nocrl -certfile bundle-$i | openssl pkcs7 -print_certs -noout done galera-4-26.4.22/tests/conf/galera-server-1.key000644 000162 177776 00000003250 14755062442 022210 0ustar00jenkinsnogroup000000 000000 -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCm59B7kWp5wahh LZ6dFGiKsCygOryOVJKvGkMnpZOp5UsBXPMPGHLQSRTSvefDg7gHc1O3pqy+KNEb 4ckrndwUqHRIYbqOeq4eM+yoxv2EHFBBFbnyUATnOoKdYfjypyuu7UWuDjSwuy4o DTxFpy7WbtaL1zZiRRL1zK4G3xV1ZL330B3cGilfT0ydjBEKfEPJDepi41ZbY7p8 t5KCNvArI8FQGgvcdZIc+wsUEN/PXE3XnGy382/Fn9apYm0DhlgszIqTCxqRZ4G9 En5WqeXgP6EWh1aOEuSPNTTHXh30pF54YsNlaoWx7gTppEqLPpyPwHJ5AwNKPkqZ QzxJ0GHnAgMBAAECggEAY371jXQWYRbM5YFoeINd/q8fKzVYihBokPG67ruJ11HU 8K8URf9xEjE+tJJ4wtiWr/pUAbreZ021ukVSwymLtNTm3O9FYwJHIcIaZlKZdlPa k7H78zM4eQ8sJd3tpdl59QGE9EmLp38t+LpLH8qV5UaVpy9YQCSlNNobNAVwqVW5 0KrxIM4tITyuvIbmn7JWkeaWi2NZjYNus80K5uEve5tYGzchA6ejuCw082pi4pHk bvl3b381l3OCQyQIegeRMp1aN3xUKG/dCW+iaHi/+io40NJQEI/n35ExLLo9xYts PBQACnPQ1JiJ2ktXVa9Rvm7KIKKHravHh31sjzUxoQKBgQDdcTDBG7YkOU8hcb/+ 1ggtGQrIeHTuhXwO5WUhNL5wqrbi97VpSji+fa5wYLULDAtX2VdG98gyPoNk324A u8LSGka/gqvj8UhV9Pgg9m38aHfsfSvy3Cvsgh+Kgrkia8HgQKqixk3KPvcK/jOs G+macG7lOfBiSh/5DXNhW8v/owKBgQDA89ZrU4rMnaQDD463p/UwDtXCQooNPuYL Nnmi12QQfn5M8A2j48ZbvFdX5FnS8rnw+zebYMVTct6GCEuGDuLgdXOLjMcJTVkj s0LVyYco9RQ/Vq7CiFvC0hPMnihpsyDqUFsUIRDdDGtjX3483zVMDR/1GNO4nXBM OutgSSPo7QKBgDJI28teIZREN0Xe8LxLmfuzrhEr2VG4mh0/1iEeOwiWm4UavuUz 54LKQ2xdaX8iswi13+3LonhWXbvwSKh9+pV/RgWDBMl0Jvrt79J8YyloB6N6IRq7 CHZibgsj+Cpq0dG8nYLtCQkeFzc11kIE7J0XXvdKmt2W+3+woDzBfaeFAoGBAKmv RieIho2/LFdMvTOhzyv+P9ngbkuLvv6pX3rndKZUt3mtujEgxHY0QZOXy66Jqx/T rWlFRwNGB49TiCdSvA6s+3oDyZ3Smcudf+9GlUhdjvrMnk+RXzELUkIl7GBYGWA2 XoJrOctLer0fY4LgwJwrBqmitb6rJfDD9+7rI1tZAoGBAKKEOygxVoB+2o3dLpua QsoeygGZG+IWeyJTERF/dOf3g0t36I/liw7eDuOEuZgMs83m2FIbmS+ycdOcVDmR pvjCpRMwd3J9YD21/ej+daoQaGrdA0XX1GAYWKmQlwl3YDhOQ/U9x7k/UxBAzb3f ZsxB8hqvPnwd5NGEhztBCl0r -----END PRIVATE KEY----- galera-4-26.4.22/tests/conf/my.cnf.3.tmpl000644 000162 177776 00000000141 14755062442 021026 0ustar00jenkinsnogroup000000 000000 wsrep_sst_method=mysqldump wsrep_sst_auth=root:rootpass wsrep_sst_receive_address=127.0.1.1:3313 galera-4-26.4.22/tests/conf/my.cnf.1.tmpl000644 000162 177776 00000000136 14755062442 021030 0ustar00jenkinsnogroup000000 000000 wsrep_sst_method=rsync wsrep_sst_auth=root:rootpass wsrep_sst_receive_address=127.0.1.1:10013 galera-4-26.4.22/tests/conf/bundle-galera-server-2.pem000644 000162 177776 00000006766 14755062442 023470 0ustar00jenkinsnogroup000000 000000 -----BEGIN CERTIFICATE----- MIIFBTCCA+2gAwIBAgIQCXWJCSwVSlnhRIBsZRqhoTANBgkqhkiG9w0BAQsFADCB nTELMAkGA1UEBhMCRkkxEDAOBgNVBAgMB1V1c2ltYWExETAPBgNVBAcMCEhlbHNp bmtpMRUwEwYDVQQKDAxDb2RlcnNoaXAgT3kxFTATBgNVBAsMDEdhbGVyYSBEZXZl bDETMBEGA1UEAwwKR2FsZXJhIEludDEmMCQGCSqGSIb3DQEJARYXZGV2ZWxAZ2Fs ZXJhY2x1c3Rlci5jb20wHhcNMjEwMTIxMTAzMDA2WhcNMjQwMTA2MTAzMDA2WjCB ojELMAkGA1UEBhMCRkkxEDAOBgNVBAgMB1V1c2ltYWExETAPBgNVBAcMCEhlbHNp bmtpMRUwEwYDVQQKDAxDb2RlcnNoaXAgT3kxFTATBgNVBAsMDEdhbGVyYSBEZXZl bDEYMBYGA1UEAwwPZ2FsZXJhLXNlcnZlci0yMSYwJAYJKoZIhvcNAQkBFhdkZXZl bEBnYWxlcmFjbHVzdGVyLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBANe/MV8pWNQhcjaCIFgOZpshRB7z12EJeKq7jrZDHvePzlurD+BhtWKyFvS6 l5hYD/vRyU4vMCevamKCOsuW0kU1l8iveadjDMtxVhMjqcY2JDjW5ehpCoq96UGs Mzs8ftTCziOQolXkcAGNJRaKddc/0p5CsknIIDpxNs4dXnGAuZZuLNXF6fpVcFk9 uc36uaD2OKH0vy4fCE0xravi4jYz0vkrOm6UtBzxaLn+EH51GwtCcvxkrQadbssg /F+thkImzfA56FDfebcdNqHINa5KoPXlw8AsJ7biB1hPGb2ixi/KKRNFMko8LQKZ FbHNmYm1xS8QH+CnCWOVdBPginUCAwEAAaOCATgwggE0MAkGA1UdEwQCMAAwHQYD VR0OBBYEFLnNuTsBB87YXjeWHmunNOB4YHMWMIHeBgNVHSMEgdYwgdOAFMcfdZZd WkrNcas5eD4dgC20E8L/oYGnpIGkMIGhMQswCQYDVQQGEwJGSTEQMA4GA1UECAwH VXVzaW1hYTERMA8GA1UEBwwISGVsc2lua2kxFTATBgNVBAoMDENvZGVyc2hpcCBP eTEVMBMGA1UECwwMR2FsZXJhIERldmVsMRcwFQYDVQQDDA5HYWxlcmEgUm9vdCBD QTEmMCQGCSqGSIb3DQEJARYXZGV2ZWxAZ2FsZXJhY2x1c3Rlci5jb22CEQCSzpcF 09pEs84e19r5/uCQMAsGA1UdDwQEAwIFoDAaBgNVHREEEzARgg9nYWxlcmEtc2Vy dmVyLTIwDQYJKoZIhvcNAQELBQADggEBAJX3x2zcK3+y+dgkVN6AZjaQfFJC6keG rw2B4xnl+Gv3x0cXZFcNjjAg9iNgzU6z6Ne+sAdC4CQERiLsb8hKQANG0wgIHp9H Goz6q+ssO5psjHKVIw9V3WsNZdJK2kVYj+VcSLfcSmO6WI17NyZuSv6d88RS2OVs HCZ/a5jwuVF66NUz/LF3JTqsiKsjgddgnFOymytyYqxS6Y5wgBrKjlTU+HVWfTae EpozWDVL0K39UrEY8M2JCBKcZajq7leHYmYc9NdATVyIm/t0hAJYMRrMkxyGvUT3 arwkBwH1SYUNqHrdVZnnNb5ojO6IKE1RkNUrYRahouJ/6ubGb23TQ6c= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIE7zCCA9egAwIBAgIRAJLOlwXT2kSzzh7X2vn+4JAwDQYJKoZIhvcNAQELBQAw gaExCzAJBgNVBAYTAkZJMRAwDgYDVQQIDAdVdXNpbWFhMREwDwYDVQQHDAhIZWxz aW5raTEVMBMGA1UECgwMQ29kZXJzaGlwIE95MRUwEwYDVQQLDAxHYWxlcmEgRGV2 ZWwxFzAVBgNVBAMMDkdhbGVyYSBSb290IENBMSYwJAYJKoZIhvcNAQkBFhdkZXZl bEBnYWxlcmFjbHVzdGVyLmNvbTAeFw0yMTAxMjExMDMwMDZaFw0yNDAxMDYxMDMw MDZaMIGdMQswCQYDVQQGEwJGSTEQMA4GA1UECAwHVXVzaW1hYTERMA8GA1UEBwwI SGVsc2lua2kxFTATBgNVBAoMDENvZGVyc2hpcCBPeTEVMBMGA1UECwwMR2FsZXJh IERldmVsMRMwEQYDVQQDDApHYWxlcmEgSW50MSYwJAYJKoZIhvcNAQkBFhdkZXZl bEBnYWxlcmFjbHVzdGVyLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBAM67vDBE4ALnYbfDKtAphdd/rFSmtiaOmZcl58RAPpaaQQ/jxLwNvVgsaCh/ 2boWBXhGNCzB175sxcK2XkKEn9MHENARKDzfdFoIQjPqQyZxX6CupYOdnA/B9+l/ +6uW7Iu+N6UP+IUeW/ElWzbIh5k/mpzOr16r/MCmxD9dvB7i1C5+bvXB6lasN2ot tldw6cQtrzGBmYRl3f/hUq3j9gwrPm3SVfuoEPCoUetobgUZbKdi0jHuYRi4y60T +Vn7Xx1fcPz9qwyWJHtw9ERr/6KWhxOk9+rxiUmNIah337p+nOe282lCmnI+hMLo CSeyjlPVzIxQY5KjTFW4zu6wTWkCAwEAAaOCASIwggEeMAwGA1UdEwQFMAMBAf8w HQYDVR0OBBYEFMcfdZZdWkrNcas5eD4dgC20E8L/MIHhBgNVHSMEgdkwgdaAFMQb 2PuGq8uVODtrOl5vGvhP/0rwoYGnpIGkMIGhMQswCQYDVQQGEwJGSTEQMA4GA1UE CAwHVXVzaW1hYTERMA8GA1UEBwwISGVsc2lua2kxFTATBgNVBAoMDENvZGVyc2hp cCBPeTEVMBMGA1UECwwMR2FsZXJhIERldmVsMRcwFQYDVQQDDA5HYWxlcmEgUm9v dCBDQTEmMCQGCSqGSIb3DQEJARYXZGV2ZWxAZ2FsZXJhY2x1c3Rlci5jb22CFHkh Yqjh/DAHX8QPlm+UBTerUrwzMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOC AQEAaJuugEguz+T4V8umj0zAv/yfWOw+647scCJpfF1P2q2GSTDGG3LkjrDuKMK+ zp0hAHdXhDD8XDOh2q6eMbUTCVUFqnM6ss4os/HyK5f5UCv3gJaSXJm3GAGgmZze HsdDPRTePp1Mr21EjChsWEUAb6EKA5F6ezUVglFwi4uD00FNYA/If7mbizomohS9 JvXPhriy9cB1jaLgP5UOl1tT3CJUjNY2Jdk3RKOIEthUaHxY7xnJVLGrLQrMBjG7 dKPuxucxvpKBAMhqnLdNVBYg3wb7WAzAMVedUrJWpXaWzWIsjLtadt6M3kCwYKtS h3vB5D/rqXSP0pdm50ihb3yRHA== -----END CERTIFICATE----- galera-4-26.4.22/tests/conf/bundle-galera-server-3.pem000644 000162 177776 00000006766 14755062442 023471 0ustar00jenkinsnogroup000000 000000 -----BEGIN CERTIFICATE----- MIIFBTCCA+2gAwIBAgIQL1zVZra4T1YP2fglm7GuXjANBgkqhkiG9w0BAQsFADCB nTELMAkGA1UEBhMCRkkxEDAOBgNVBAgMB1V1c2ltYWExETAPBgNVBAcMCEhlbHNp bmtpMRUwEwYDVQQKDAxDb2RlcnNoaXAgT3kxFTATBgNVBAsMDEdhbGVyYSBEZXZl bDETMBEGA1UEAwwKR2FsZXJhIEludDEmMCQGCSqGSIb3DQEJARYXZGV2ZWxAZ2Fs ZXJhY2x1c3Rlci5jb20wHhcNMjEwMTIxMTAzMDA2WhcNMjQwMTA2MTAzMDA2WjCB ojELMAkGA1UEBhMCRkkxEDAOBgNVBAgMB1V1c2ltYWExETAPBgNVBAcMCEhlbHNp bmtpMRUwEwYDVQQKDAxDb2RlcnNoaXAgT3kxFTATBgNVBAsMDEdhbGVyYSBEZXZl bDEYMBYGA1UEAwwPZ2FsZXJhLXNlcnZlci0zMSYwJAYJKoZIhvcNAQkBFhdkZXZl bEBnYWxlcmFjbHVzdGVyLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBALSDwuyokegxqwcFl5XJ1oKhjkeWQ4P8+Mnkc+e4nWEiIERltY9ZyD2xpYT3 fHkrI7TcqqCyoi3ZpEb9TChhg7VnMXCBPxWgOBnoUEeCN08G0PLbV++xuUQv3XCo tkQxaVNwzXYkqzo1vmgjjDBSKtVF8hZcYr26vPDYUraXHziZwrHSHL/PdncQDPNo U+iiqJyJa9JiftbVCnPWv/M12SCc7F5NwQhoL5+s5iJJ+DGZeB9z4ZITp3kFwbiR vvsloD2sT6gzv1NuXQ2D4gPZA9Ly37PesS61oQ4lUhWt4Zt2Rnz5wIcABIyoMvE5 X27zoQU2bauCIQ/NGDfsrfJIvMUCAwEAAaOCATgwggE0MAkGA1UdEwQCMAAwHQYD VR0OBBYEFECqB4235rqo8UaJ6U9fnY2xQJcdMIHeBgNVHSMEgdYwgdOAFMcfdZZd WkrNcas5eD4dgC20E8L/oYGnpIGkMIGhMQswCQYDVQQGEwJGSTEQMA4GA1UECAwH VXVzaW1hYTERMA8GA1UEBwwISGVsc2lua2kxFTATBgNVBAoMDENvZGVyc2hpcCBP eTEVMBMGA1UECwwMR2FsZXJhIERldmVsMRcwFQYDVQQDDA5HYWxlcmEgUm9vdCBD QTEmMCQGCSqGSIb3DQEJARYXZGV2ZWxAZ2FsZXJhY2x1c3Rlci5jb22CEQCSzpcF 09pEs84e19r5/uCQMAsGA1UdDwQEAwIFoDAaBgNVHREEEzARgg9nYWxlcmEtc2Vy dmVyLTMwDQYJKoZIhvcNAQELBQADggEBAB+34pTwIw8mObbi9GjKRw0eXEm7GRTY iz+I2QXGaZq71GHvGd3iyGgmdwz8ffwAdd4BuZjNECMYueBrDnNdYz8ilRjfEQQN OkNOfjt17p/NHE0FksSLPAIi9ewaUDL/oVvPjQdKpJehv2pcsSTvK9YNfHAdWDWL 2uM3vsyUyyoFqjLepgCcxPbL7Ar+zC/c53Z/YTAjrv7/9lc0nD2P/7MKb3v5/OJb qheijRBfRu4jmKkGuSwRUv4U7QJMha8G0Pe7+KrTmw9cXBDffKjuase5Z+Oe/swk Zgg7b4BTHZnllKDZvCerGibHBA7SPlxdrH0GGt7VKL21liNZ4psqywo= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIE7zCCA9egAwIBAgIRAJLOlwXT2kSzzh7X2vn+4JAwDQYJKoZIhvcNAQELBQAw gaExCzAJBgNVBAYTAkZJMRAwDgYDVQQIDAdVdXNpbWFhMREwDwYDVQQHDAhIZWxz aW5raTEVMBMGA1UECgwMQ29kZXJzaGlwIE95MRUwEwYDVQQLDAxHYWxlcmEgRGV2 ZWwxFzAVBgNVBAMMDkdhbGVyYSBSb290IENBMSYwJAYJKoZIhvcNAQkBFhdkZXZl bEBnYWxlcmFjbHVzdGVyLmNvbTAeFw0yMTAxMjExMDMwMDZaFw0yNDAxMDYxMDMw MDZaMIGdMQswCQYDVQQGEwJGSTEQMA4GA1UECAwHVXVzaW1hYTERMA8GA1UEBwwI SGVsc2lua2kxFTATBgNVBAoMDENvZGVyc2hpcCBPeTEVMBMGA1UECwwMR2FsZXJh IERldmVsMRMwEQYDVQQDDApHYWxlcmEgSW50MSYwJAYJKoZIhvcNAQkBFhdkZXZl bEBnYWxlcmFjbHVzdGVyLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBAM67vDBE4ALnYbfDKtAphdd/rFSmtiaOmZcl58RAPpaaQQ/jxLwNvVgsaCh/ 2boWBXhGNCzB175sxcK2XkKEn9MHENARKDzfdFoIQjPqQyZxX6CupYOdnA/B9+l/ +6uW7Iu+N6UP+IUeW/ElWzbIh5k/mpzOr16r/MCmxD9dvB7i1C5+bvXB6lasN2ot tldw6cQtrzGBmYRl3f/hUq3j9gwrPm3SVfuoEPCoUetobgUZbKdi0jHuYRi4y60T +Vn7Xx1fcPz9qwyWJHtw9ERr/6KWhxOk9+rxiUmNIah337p+nOe282lCmnI+hMLo CSeyjlPVzIxQY5KjTFW4zu6wTWkCAwEAAaOCASIwggEeMAwGA1UdEwQFMAMBAf8w HQYDVR0OBBYEFMcfdZZdWkrNcas5eD4dgC20E8L/MIHhBgNVHSMEgdkwgdaAFMQb 2PuGq8uVODtrOl5vGvhP/0rwoYGnpIGkMIGhMQswCQYDVQQGEwJGSTEQMA4GA1UE CAwHVXVzaW1hYTERMA8GA1UEBwwISGVsc2lua2kxFTATBgNVBAoMDENvZGVyc2hp cCBPeTEVMBMGA1UECwwMR2FsZXJhIERldmVsMRcwFQYDVQQDDA5HYWxlcmEgUm9v dCBDQTEmMCQGCSqGSIb3DQEJARYXZGV2ZWxAZ2FsZXJhY2x1c3Rlci5jb22CFHkh Yqjh/DAHX8QPlm+UBTerUrwzMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOC AQEAaJuugEguz+T4V8umj0zAv/yfWOw+647scCJpfF1P2q2GSTDGG3LkjrDuKMK+ zp0hAHdXhDD8XDOh2q6eMbUTCVUFqnM6ss4os/HyK5f5UCv3gJaSXJm3GAGgmZze HsdDPRTePp1Mr21EjChsWEUAb6EKA5F6ezUVglFwi4uD00FNYA/If7mbizomohS9 JvXPhriy9cB1jaLgP5UOl1tT3CJUjNY2Jdk3RKOIEthUaHxY7xnJVLGrLQrMBjG7 dKPuxucxvpKBAMhqnLdNVBYg3wb7WAzAMVedUrJWpXaWzWIsjLtadt6M3kCwYKtS h3vB5D/rqXSP0pdm50ihb3yRHA== -----END CERTIFICATE----- galera-4-26.4.22/tests/conf/galera-server-3.key000644 000162 177776 00000003250 14755062442 022212 0ustar00jenkinsnogroup000000 000000 -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0g8LsqJHoMasH BZeVydaCoY5HlkOD/PjJ5HPnuJ1hIiBEZbWPWcg9saWE93x5KyO03KqgsqIt2aRG /UwoYYO1ZzFwgT8VoDgZ6FBHgjdPBtDy21fvsblEL91wqLZEMWlTcM12JKs6Nb5o I4wwUirVRfIWXGK9urzw2FK2lx84mcKx0hy/z3Z3EAzzaFPooqiciWvSYn7W1Qpz 1r/zNdkgnOxeTcEIaC+frOYiSfgxmXgfc+GSE6d5BcG4kb77JaA9rE+oM79Tbl0N g+ID2QPS8t+z3rEutaEOJVIVreGbdkZ8+cCHAASMqDLxOV9u86EFNm2rgiEPzRg3 7K3ySLzFAgMBAAECggEAMnuGz9h0tZLuWZBezC0eKHo16B9F0mu0yAFzyKIAoWGB 1J79H5HkRhygRYdQ8DH4UmRD850BbgEnyBKeccyToO+zC9uZYNrl1Zj6moT4Ismt Nb3R4d66CS+5pgAIuRl4czVgwGGQ4T6WeVk3o2vXPr3I7if4FUdb/57/wsCQoqsl WyEz7OUuM+6nI4cTnHO2fSctJlhDo6WCEoKHtLmwvgVJrCSJYaPFWdn7rzuKyZMY n/55E1crOSn3f+Wnm0x17Lv/vFXsBS9dYctj0mx17oMoFPePc/6O6MK4Fa3nojN8 0vrKCVwz9CBxbYow0B6DyiWFZn68Z3LeiPyqOBgZAQKBgQDb1/90IB+mU+ay1B48 ztL4g0xA5htxK1gz+HoH0niQ9u/OjzJYNwl24N61UZnmqygHMIQhV+Cq3bBDCfiP Roh/EG7ue2tws8IcadYQIXUTvSK9zcJ2JyfH/Qx4/llFPYPQQ8kdb5ZWNTve6TSk vREiNrmwMZhFqaxdiDak60IM6QKBgQDSM+ggU1oQX4vA5N+czmUoNW4wqAogY0lb V6b/GB2jdbAjQ622uirjPYeauq97ReLUAUZV741n38JSaAR3GFeb9stz1MNGFoTF mH55hvtXvWkDuhbOJL8V3R1OTXEH3GUfT1H6Tb5rLiEMbuHG9ETF+gxVnLgXbfW8 pNPTUzOXfQKBgQCUWyT2dp8lHUV3tJFbM69HKUOSIDawnjF9kNa45J2cJigaWqP8 x9dJM+LWtWSIN0Rh5amk3qqsY8II23ezKEQAi1Rw9zS/7260we9FH27kjSQetfXe yfmcifWayPnxMdv79WKIzL7FGlN20CVIbpZbYlbYwf8iM6gvzjYiAOin0QKBgClN M6XuWrru2xnanqlD0JJ29SCwU9ULTSWacmCbD2/HtwUmziIiMD0YIOXhGovBLpFz cqt99y3axGbGs2HnMYeelVk8C+ZglFPy457jS81wlQq7bLGyfK1CFfkFtFjUEDZ3 smbZEbojhTDZNJmP81dnCzdjJLTN0UPwqwJRzchNAoGAIb/BHjS2jptloFlR6wQf 6uReMpvYLTleBYp+j44UNqWG/Sj06pF4qFWqdN4Lflxk8BlfpZcBilp/Juc6StF7 yzl5vFDAfPo7uFUVrWhGqPbPtemnyFeZLnFBCHIORYJPCM3SEe/StHIg+GGKT2gy 4L1loGk3pJQdHTc7MDoNmBE= -----END PRIVATE KEY----- galera-4-26.4.22/tests/conf/galera-server-2.key000644 000162 177776 00000003250 14755062442 022211 0ustar00jenkinsnogroup000000 000000 -----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDXvzFfKVjUIXI2 giBYDmabIUQe89dhCXiqu462Qx73j85bqw/gYbVishb0upeYWA/70clOLzAnr2pi gjrLltJFNZfIr3mnYwzLcVYTI6nGNiQ41uXoaQqKvelBrDM7PH7Uws4jkKJV5HAB jSUWinXXP9KeQrJJyCA6cTbOHV5xgLmWbizVxen6VXBZPbnN+rmg9jih9L8uHwhN Ma2r4uI2M9L5KzpulLQc8Wi5/hB+dRsLQnL8ZK0GnW7LIPxfrYZCJs3wOehQ33m3 HTahyDWuSqD15cPALCe24gdYTxm9osYvyikTRTJKPC0CmRWxzZmJtcUvEB/gpwlj lXQT4Ip1AgMBAAECggEAK0r7fkwOviqj/5onIWRqZJDNWaS2wIslAqW2Yo6fhS// SdfOzMjunAp+nsdeqjxpoK+dRKolcKE7qN7XK4ltUmQJvESyFcvDgoOQsTCD3fl5 VUqSQgvqCKNJTltewHFIRit9MBfS64d2jmkjWx2XvgTZn9ZkbnfRN8kGJWAxVncO 10ltBpt7Kbg4sFdYP6hNJoY2UKJFwAxto/jyX6uDtb1kSgyGRPDpsERGhKzYjj5z izrxeNa1JVPIBN3f2Sb0d+ZaIew46OSMpMkQ8xnKNXtEFH01T4m2+LFclAIM25MO kRpRl7UUF4RnSStyB2q89BTOqpr8jcoZ9oksdyp6dQKBgQD297oJTQT/tTDuixIP O5SyiLxzUXH6WfXB3cpxP5y8quzuzzsUTx7lspBzhh93/1J2btedTMW86eFsoqNI uf3nk8ZyhX6vJWfpqb1sIYh3yJBlN3GZfMweUzupmO4jU0MsfQayZ09cKuR3zUMt ujcYVmYZdgfLD6eKPDU0Q/osEwKBgQDfoyf+2/Yy1g+co2cVyqokyLL9a0jtY9t0 j/B3S9N2wIjSnb8CkYxMBwYh+dBwc/Eu+6LxLTGdtg029RU2daWzEKosG9Py2wsR LztHD9sxXZdRzc6W6LN6deIaSj0H01s3vcOgDm0++ap2ZjNyc85nhy0fieRkw3nJ xlqQqwgwVwKBgBPbBNz8kTtRwPZcvf31h1X3QldvI9/B3c1RXJZQS/SrNVNZ2+Ed H1nOSmjPTrZiE6or7S/bXUZ780C9rq0JLw2wRtMsQmwocLtLh0wrQgKHYHySwZJa gzqo7HINNpAmgI8SGji7r5i0Zhvvp8gEYauWPq0rXSMJRioJ/ykNkg57AoGAGWpZ aUVmPXDGZW7MkFVv8K4+aT6AEzp9/kk9ctFTPvOymP0EcC5KW3mQ4NubLKyAhG5k njQcp2fBKLXBq2bDZg5GyKyA8eCi6VkMy46pwnp4b/uLturLOueawpIdTX5fp34R dWcuUzHchYgn4KH4mxtprWPmaO0uMhgwwrAtRhECgYB2leEWwtWveznBv5e5ZnGJ Ku2Zyj+FCv332YmjQO4r77o4C5UI83FoRIAYAGBKUwZgRhrc/7bvzjCEWG21Iq0g 94Odtcrvz+NxqVYsm+IYkGPiqdOi9heSPFUZkq8qdieft3HZlYxCBUC9hxuiWCsT HYiY2+4qEjcnfLn8B/K/rQ== -----END PRIVATE KEY----- galera-4-26.4.22/tests/conf/galera_cert.pem000644 000162 177776 00000002214 14755062442 021553 0ustar00jenkinsnogroup000000 000000 -----BEGIN CERTIFICATE----- MIIDLjCCAhYCAQEwDQYJKoZIhvcNAQELBQAwXTELMAkGA1UEBhMCRkkxETAPBgNV BAgMCEhlbHNpbmtpMREwDwYDVQQHDAhIZWxzaW5raTESMBAGA1UECgwJQ29kZXJz aGlwMRQwEgYDVQQDDAtHYWxlcmEgUm9vdDAeFw0yMTAyMjgxOTQ4NTNaFw0yMzEx MjUxOTQ4NTNaMF0xCzAJBgNVBAYTAkZJMREwDwYDVQQIDAhIZWxzaW5raTERMA8G A1UEBwwISGVsc2lua2kxEjAQBgNVBAoMCUNvZGVyc2hpcDEUMBIGA1UEAwwLR2Fs ZXJhIE5vZGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHrDam1TsH QPm+E7tZcI8dGpa0wTooKh3WWPRIjcpOuDXlV8cxyK3YZAmquAT4iWcJQZZq8t+/ LrpGK5i9sQl8dAjLftJKIRquEdwO027xF0hsqPiDMvfbBoy8af78tf3d3AFvq+Sw L5jLnTOYkAqEYXbK6m64/mAN0npyrqX9ULsPQkQggnbZYYJfRa3V3I2QfLMlFD7Y 8oy5fZTKOUOf8F1neCGNlMtGXWl3I3CW13uVyS2JmZSmI+iPtxmFOunLK+ymfmmH +6/v2EGOPZ2JNbQ1jLFPlLMfgKamSPzkI0U0jAsomFtiJqUceJFdl+WAQXKc7OUW yd5rGYvRJmGdAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEw6yadB3SwUzRQsPkSs BhlpbbukSLwWXomvVIy6eTWGCbOxA5RsXaiiNbnGY1DfYsRZJnJjZzTdQ7KWWIIC 4SB2AxOGu7GyfQFXLF8JcNJ3tTwsrQO4s1Rv3ZSVrtymOjyRXgzAWX/chek4cBzB AzqbI6PdIDDE7JEWJgj71q/pksUvaefviXrINQfkOo8PWvVWUH1Gw+dUi9Jj3JQd CrMzFQBoVgzzICRjxZoG1XtljMeoepqpbMwjp589JUNRyAmvy1yrMnxablLdLPdO zehLvs2rVXYo3vK8MAHz4OLa41Uj/0KsKhKdFgWrxGoe1mYR58wy3+Yni8g+1hbt k64= -----END CERTIFICATE----- galera-4-26.4.22/tests/conf/main.conf000644 000162 177776 00000002403 14755062442 020373 0ustar00jenkinsnogroup000000 000000 #declare -r BASE_LOG="$TEST_BASE/log" declare -r BASE_OUT="$TEST_BASE/out" mkdir -p "$BASE_OUT" declare -r BASE_RUN="$TEST_BASE/run" mkdir -p "$BASE_RUN" declare -r BASE_CONF="$TEST_BASE/conf" . ${CLUSTER_CONF:-"$BASE_CONF/cluster.conf"} if [ "$(uname -s)" == "Darwin" ]; then GLB_PRELOAD=${DYLD_INSERT_LIBRARIES:-""} else GLB_PRELOAD=${LD_PRELOAD:-""} fi export GLB_PRELOAD # The code below tries to find available libglb.so and if found, export # necessary variables for client side load balancing GLB_LIB=${GLB_LIB:-""} if [ -z "$GLB_LIB" ] then if [ -r /usr/local/lib/libglb.so ] then GLB_LIB="/usr/local/lib/libglb.so" elif [ -r /usr/lib/libglb.so ] then GLB_LIB="/usr/lib/libglb.so" fi fi if [ -r "$GLB_LIB" ] then if [ -n "$GLB_PRELOAD" ] then export GLB_PRELOAD="$GLB_LIB:$GLB_PRELOAD" else export GLB_PRELOAD="$GLB_LIB" fi export GLB_BIND=$DBMS_HOST:$DBMS_PORT GLB_TARGETS="" for node in $NODE_LIST do target=${NODE_INCOMING_HOST[$node]}:${NODE_INCOMING_PORT[$node]} if [ $node -ne $NODE_MAX ] then GLB_TARGETS="$GLB_TARGETS$target," else GLB_TARGETS="$GLB_TARGETS$target" fi done export GLB_TARGETS fi galera-4-26.4.22/tests/conf/galera-server-2.pem000644 000162 177776 00000012411 14755062442 022201 0ustar00jenkinsnogroup000000 000000 Certificate: Data: Version: 3 (0x2) Serial Number: 09:75:89:09:2c:15:4a:59:e1:44:80:6c:65:1a:a1:a1 Signature Algorithm: sha256WithRSAEncryption Issuer: C=FI, ST=Uusimaa, L=Helsinki, O=Codership Oy, OU=Galera Devel, CN=Galera Int/emailAddress=devel@galeracluster.com Validity Not Before: Jan 21 10:30:06 2021 GMT Not After : Jan 6 10:30:06 2024 GMT Subject: C=FI, ST=Uusimaa, L=Helsinki, O=Codership Oy, OU=Galera Devel, CN=galera-server-2/emailAddress=devel@galeracluster.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:d7:bf:31:5f:29:58:d4:21:72:36:82:20:58:0e: 66:9b:21:44:1e:f3:d7:61:09:78:aa:bb:8e:b6:43: 1e:f7:8f:ce:5b:ab:0f:e0:61:b5:62:b2:16:f4:ba: 97:98:58:0f:fb:d1:c9:4e:2f:30:27:af:6a:62:82: 3a:cb:96:d2:45:35:97:c8:af:79:a7:63:0c:cb:71: 56:13:23:a9:c6:36:24:38:d6:e5:e8:69:0a:8a:bd: e9:41:ac:33:3b:3c:7e:d4:c2:ce:23:90:a2:55:e4: 70:01:8d:25:16:8a:75:d7:3f:d2:9e:42:b2:49:c8: 20:3a:71:36:ce:1d:5e:71:80:b9:96:6e:2c:d5:c5: e9:fa:55:70:59:3d:b9:cd:fa:b9:a0:f6:38:a1:f4: bf:2e:1f:08:4d:31:ad:ab:e2:e2:36:33:d2:f9:2b: 3a:6e:94:b4:1c:f1:68:b9:fe:10:7e:75:1b:0b:42: 72:fc:64:ad:06:9d:6e:cb:20:fc:5f:ad:86:42:26: cd:f0:39:e8:50:df:79:b7:1d:36:a1:c8:35:ae:4a: a0:f5:e5:c3:c0:2c:27:b6:e2:07:58:4f:19:bd:a2: c6:2f:ca:29:13:45:32:4a:3c:2d:02:99:15:b1:cd: 99:89:b5:c5:2f:10:1f:e0:a7:09:63:95:74:13:e0: 8a:75 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Subject Key Identifier: B9:CD:B9:3B:01:07:CE:D8:5E:37:96:1E:6B:A7:34:E0:78:60:73:16 X509v3 Authority Key Identifier: keyid:C7:1F:75:96:5D:5A:4A:CD:71:AB:39:78:3E:1D:80:2D:B4:13:C2:FF DirName:/C=FI/ST=Uusimaa/L=Helsinki/O=Codership Oy/OU=Galera Devel/CN=Galera Root CA/emailAddress=devel@galeracluster.com serial:92:CE:97:05:D3:DA:44:B3:CE:1E:D7:DA:F9:FE:E0:90 X509v3 Key Usage: Digital Signature, Key Encipherment X509v3 Subject Alternative Name: DNS:galera-server-2 Signature Algorithm: sha256WithRSAEncryption 95:f7:c7:6c:dc:2b:7f:b2:f9:d8:24:54:de:80:66:36:90:7c: 52:42:ea:47:86:af:0d:81:e3:19:e5:f8:6b:f7:c7:47:17:64: 57:0d:8e:30:20:f6:23:60:cd:4e:b3:e8:d7:be:b0:07:42:e0: 24:04:46:22:ec:6f:c8:4a:40:03:46:d3:08:08:1e:9f:47:1a: 8c:fa:ab:eb:2c:3b:9a:6c:8c:72:95:23:0f:55:dd:6b:0d:65: d2:4a:da:45:58:8f:e5:5c:48:b7:dc:4a:63:ba:58:8d:7b:37: 26:6e:4a:fe:9d:f3:c4:52:d8:e5:6c:1c:26:7f:6b:98:f0:b9: 51:7a:e8:d5:33:fc:b1:77:25:3a:ac:88:ab:23:81:d7:60:9c: 53:b2:9b:2b:72:62:ac:52:e9:8e:70:80:1a:ca:8e:54:d4:f8: 75:56:7d:36:9e:12:9a:33:58:35:4b:d0:ad:fd:52:b1:18:f0: cd:89:08:12:9c:65:a8:ea:ee:57:87:62:66:1c:f4:d7:40:4d: 5c:88:9b:fb:74:84:02:58:31:1a:cc:93:1c:86:bd:44:f7:6a: bc:24:07:01:f5:49:85:0d:a8:7a:dd:55:99:e7:35:be:68:8c: ee:88:28:4d:51:90:d5:2b:61:16:a1:a2:e2:7f:ea:e6:c6:6f: 6d:d3:43:a7 -----BEGIN CERTIFICATE----- MIIFBTCCA+2gAwIBAgIQCXWJCSwVSlnhRIBsZRqhoTANBgkqhkiG9w0BAQsFADCB nTELMAkGA1UEBhMCRkkxEDAOBgNVBAgMB1V1c2ltYWExETAPBgNVBAcMCEhlbHNp bmtpMRUwEwYDVQQKDAxDb2RlcnNoaXAgT3kxFTATBgNVBAsMDEdhbGVyYSBEZXZl bDETMBEGA1UEAwwKR2FsZXJhIEludDEmMCQGCSqGSIb3DQEJARYXZGV2ZWxAZ2Fs ZXJhY2x1c3Rlci5jb20wHhcNMjEwMTIxMTAzMDA2WhcNMjQwMTA2MTAzMDA2WjCB ojELMAkGA1UEBhMCRkkxEDAOBgNVBAgMB1V1c2ltYWExETAPBgNVBAcMCEhlbHNp bmtpMRUwEwYDVQQKDAxDb2RlcnNoaXAgT3kxFTATBgNVBAsMDEdhbGVyYSBEZXZl bDEYMBYGA1UEAwwPZ2FsZXJhLXNlcnZlci0yMSYwJAYJKoZIhvcNAQkBFhdkZXZl bEBnYWxlcmFjbHVzdGVyLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBANe/MV8pWNQhcjaCIFgOZpshRB7z12EJeKq7jrZDHvePzlurD+BhtWKyFvS6 l5hYD/vRyU4vMCevamKCOsuW0kU1l8iveadjDMtxVhMjqcY2JDjW5ehpCoq96UGs Mzs8ftTCziOQolXkcAGNJRaKddc/0p5CsknIIDpxNs4dXnGAuZZuLNXF6fpVcFk9 uc36uaD2OKH0vy4fCE0xravi4jYz0vkrOm6UtBzxaLn+EH51GwtCcvxkrQadbssg /F+thkImzfA56FDfebcdNqHINa5KoPXlw8AsJ7biB1hPGb2ixi/KKRNFMko8LQKZ FbHNmYm1xS8QH+CnCWOVdBPginUCAwEAAaOCATgwggE0MAkGA1UdEwQCMAAwHQYD VR0OBBYEFLnNuTsBB87YXjeWHmunNOB4YHMWMIHeBgNVHSMEgdYwgdOAFMcfdZZd WkrNcas5eD4dgC20E8L/oYGnpIGkMIGhMQswCQYDVQQGEwJGSTEQMA4GA1UECAwH VXVzaW1hYTERMA8GA1UEBwwISGVsc2lua2kxFTATBgNVBAoMDENvZGVyc2hpcCBP eTEVMBMGA1UECwwMR2FsZXJhIERldmVsMRcwFQYDVQQDDA5HYWxlcmEgUm9vdCBD QTEmMCQGCSqGSIb3DQEJARYXZGV2ZWxAZ2FsZXJhY2x1c3Rlci5jb22CEQCSzpcF 09pEs84e19r5/uCQMAsGA1UdDwQEAwIFoDAaBgNVHREEEzARgg9nYWxlcmEtc2Vy dmVyLTIwDQYJKoZIhvcNAQELBQADggEBAJX3x2zcK3+y+dgkVN6AZjaQfFJC6keG rw2B4xnl+Gv3x0cXZFcNjjAg9iNgzU6z6Ne+sAdC4CQERiLsb8hKQANG0wgIHp9H Goz6q+ssO5psjHKVIw9V3WsNZdJK2kVYj+VcSLfcSmO6WI17NyZuSv6d88RS2OVs HCZ/a5jwuVF66NUz/LF3JTqsiKsjgddgnFOymytyYqxS6Y5wgBrKjlTU+HVWfTae EpozWDVL0K39UrEY8M2JCBKcZajq7leHYmYc9NdATVyIm/t0hAJYMRrMkxyGvUT3 arwkBwH1SYUNqHrdVZnnNb5ojO6IKE1RkNUrYRahouJ/6ubGb23TQ6c= -----END CERTIFICATE----- galera-4-26.4.22/tests/scripts/000755 000162 177776 00000000000 14755062445 017346 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/tests/scripts/action.sh000644 000162 177776 00000020422 14755062442 021154 0ustar00jenkinsnogroup000000 000000 # Helper to get status variable value mysql_command() { local node=$1 if [ "${NODE_LOCATION[$node]}" = "local" ] then echo "${NODE_TEST_DIR[$node]}/mysql/bin/mysql" else echo "mysql" fi } cluster_status() { local node=$1 case "$DBMS" in "MYSQL") local command=$(mysql_command $node) local res=$($command -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ -h${NODE_INCOMING_HOST[$node]} -P${NODE_INCOMING_PORT[$node]} \ --skip-column-names -ss \ -e "SET wsrep_on=0; SHOW STATUS WHERE Variable_name LIKE 'wsrep_cluster_status' OR Variable_name LIKE 'wsrep_cluster_size'" 2>/dev/null) echo -n $res | awk '{ print $4 ":" $2; }' ;; "PGSQL"|*) return -1 esac } mysql_query() { local node=$1 local query=$2 local command=$(mysql_command $node) $command -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ -h${NODE_INCOMING_HOST[$node]} -P${NODE_INCOMING_PORT[$node]} \ --skip-column-names -ss -e "$query" } wait_node_state() { local node=$1 local state=$2 while true do local res="-1" case "$DBMS" in "MYSQL") res=$(mysql_query $node "SHOW STATUS LIKE 'wsrep_local_state'" \ | awk '{ print $2 }') ;; "PGSQL"|*) return -1 esac if [ "$res" = "$state" ]; then break; fi sleep 1 done } # # Routines to start|stop|check cluster nodes # action_cmd() { local cmd=$1 local node=${@:$#} local nargs=$(( $# - 2 )) # minus cmd and node local args="${@:2:$nargs}" # arguments range from 2 to n-1 local dir="${NODE_TEST_DIR[$node]}" case "$DBMS" in "MYSQL") echo -n "MYSQL_PORT=${NODE_INCOMING_PORT[$node]} "\ "\"$dir/mysql-galera\" $args $cmd" ;; "PGSQL"|*) return -1 ;; esac } # By convention node index is the last in the arguments list. # So we prepend command to the argument list otherwise it'll go after node # index here. start_cmd() { action_cmd "start" "$@" } stop_cmd() { action_cmd "stop" "$@" } restart_cmd() { action_cmd "restart" "$@" } check_cmd() { action_cmd "check" "$@" } dump_cmd() { action_cmd "dump" "$@" } action() { start_jobs "$@" wait_jobs } dump() { action "dump_cmd" "$@" } check() { wait_sync $NODE_LIST || true cmd="check_cmd" ! action "$cmd" "$@" # ! - to ignore possible connection error local -r prefix="$BASE_OUT/$cmd" local node local prev="" local fail="" for node in $NODE_LIST do local node_id="${NODE_ID[$node]}" local out="${prefix}_${node_id}.out" chk=$(cat "$out") # no need to check if file exists: # should be created even if command fails # echo "$node_id: ${chk%% -}" if [ -n "$chk" ] # skip 0-length checksum: the node was down then echo "$chk" | sed s/-/${node_id}/ if [ -z "$prev" ] then prev="$chk" else if [ "$prev" != "$chk" ] then fail="yes" fi fi fi done if [ -z "$fail" ] && [ -n "$prev" ]; then return 0; fi echo "Checksum failed." # for node in $NODE_LIST # do # local node_id="${NODE_ID[$node]}" # echo -n "$node_id: " # cat "${prefix}_$node_id.out" # done return 1 } # Query each node with causal reads on to make sure that slave # queue has been fully processed. # Arguments: list of nodes wait_sync() { local nodes=${@:-$NODE_LIST} local node for node in $nodes do mysql_query "$node" "SET SESSION wsrep_sync_wait=1; select 0;" 1>/dev/null done } start_node() { node_job "start_cmd" "$@" } stop_node() { node_job "stop_cmd" "$@" } restart_node() { node_job "restart_cmd" "$@" } dump_node() { node_job "dump_cmd" "$@" } # unlike bulk check this one returns error when the node could not be checked check_node() { local cmd="check_cmd" node_job "$cmd" "$@" local node_id="${NODE_ID[$1]}" cat "${BASE_OUT}/${cmd}_${node_id}.out" | sed s/-/${node_id}/ return $(cat $BASE_RUN/check_cmd_$node_id.ret) } extra_params() { local node=$1 local extra_params [ -z "$GCOMM_EXTRA_PARAMS" ] && extra_params="?" || extra_params="?${GCOMM_EXTRA_PARAMS}&" # echo "${extra_params}gmcast.listen_addr=tcp://${NODE_GCS_HOST[$node]}:${NODE_GCS_PORT[$node]}" echo "${extra_params}gmcast.listen_addr=tcp://0.0.0.0:${NODE_GCS_PORT[$node]}" } # return GCS address at which node N should connect to group gcs_address() { local node=$1 case "$GCS_TYPE" in "gcomm") local peer=$(( $node - 1 )) # select previous node as connection peer # local peer=0 # use the first node as a connection handle if [ $peer -lt 0 ]; then peer=$NODE_MAX; fi # rollover echo "'gcomm://${NODE_GCS_HOST[$peer]}:${NODE_GCS_PORT[$peer]}$(extra_params $node)'" ;; "vsbes") echo "'vsbes://$VSBES_ADDRESS'" ;; *) return 1 ;; esac } # start/restart nodes in group mode. _cluster_up() { local -r cmd=$1 shift SECONDS=0 # for wait_jobs for node in $NODE_LIST do echo "Starting ${NODE_ID[$node]}" if [ $node -eq 0 ] then # must make sure 1st node completely operational case "$GCS_TYPE" in # "gcomm") $cmd "-g 'gcomm://:${NODE_GCS_PORT[$node]}$(extra_params $node)'" "$@" 0 ;; "gcomm") $cmd "-g $(gcs_address $node) --mysql-opt --wsrep-new-cluster" "$@" 0 ;; "vsbes") $cmd "-g 'vsbes://$VSBES_ADDRESS'" "$@" 0 ;; esac else $cmd "-g $(gcs_address $node)" "$@" $node & fi done wait_jobs } # start/restart nodes in group mode. bootstrap() { SECONDS=0 # for wait_jobs local cnt=0 for node in $NODE_LIST do echo "Starting ${NODE_ID[$node]}" start_node "-g $(gcs_address $node)" "$@" $node & cnt=$(($cnt + 1)) done # TODO: Poll until all have reached non-prim for node in 0 # only one node is sufficient do while true do st=$(cluster_status $node) if test "x$st" = "xnon-Primary:$cnt" then break; fi sleep 1 done done # TODO: Figure out how to do this in DBMS indepent way case "$DBMS" in "MYSQL") mysql -u$DBMS_ROOT_USER -p$DBMS_ROOT_PSWD \ -h${NODE_INCOMING_HOST[0]} \ -P${NODE_INCOMING_PORT[0]} \ -e "SET GLOBAL wsrep_provider_options='pc.bootstrap=1'" ;; "PGSQL"|*) return -1 ;; esac # Jobs will finish when nodes reach primary wait_jobs } start() { _cluster_up start_node "$@" } _get_status_var() { # INFORMATION_SCHEMA.GLOBAL_STATUS is deprecated in MySQL >= 5.7 # SHOW GLOBAL STATUS seems to be more compatible between the versions. # mysql_query "$1" "SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = '$2'" 2>/dev/null || echo -1 mysql_query "$1" "SHOW GLOBAL STATUS LIKE '$2'" | tail -n1 | cut -f 2- [ 0 = ${PIPESTATUS[0]} ] || echo -1 } stop() { SECONDS=0 local node # stop all nodes but the first one (nearly) simultaneously for node in $(seq $NODE_MAX -1 1) do echo "Stopping ${NODE_ID[$node]}" stop_node "$@ $node" & sleep 1 done node=0 # Here we don't care if the node lost PC (it should not but it may) # If it did there is little we can do short of bootstrapping it while [ $(_get_status_var "$node" "wsrep_cluster_size") -gt 1 ] do sleep 0.2 done if [ $(_get_status_var "$node" "wsrep_cluster_status") = "non-Primary" ] then mysql_query "$node" "SET GLOBAL wsrep_provider_options='pc.bootstrap=1'" while [ $(_get_status_var "$node" "wsrep_cluster_status") = "non-Primary" ] do sleep 0.2 done fi echo "Stopping ${NODE_ID[$node]}" stop_node "$@ $node" & wait_jobs } restart() { stop _cluster_up start_node "$@" } galera-4-26.4.22/tests/scripts/install.sh000644 000162 177776 00000005613 14755062442 021352 0ustar00jenkinsnogroup000000 000000 # # Routines to install distribution on nodes # untar_cmd() { local node=${@:$#} local path="${NODE_TEST_DIR[$node]}" local base=$path/mysql local data=$base/var local hst=$(hostname -s) local SAVE="([ -d \"$data\" ] && rm -rf \"$data\".saved && mv \"$data\" \"$data\".saved || :) && " local UNTAR="mkdir -p \"$path\" && tar --strip 1 -C \"$path\" -xzf - && " local INIT="\"$base\"/bin/init_db.sh \"$base\" \"$DBMS_ROOT_PSWD\" \"$DBMS_TEST_PSWD\" || : && " local RESTORE="([ -d \"$data\".saved ] && rm -rf \"$data\" && mv \"$data\".saved \"$data\" || :) " echo -n "$SAVE $UNTAR $INIT $RESTORE" } copy_config() { local -r node=$1 local cnf local cnf_dir local ca_src="$BASE_CONF/galera_ca.pem" local ca_dst local key_src="$BASE_CONF/galera_key.pem" local key_dst local cert_src="$BASE_CONF/galera_cert.pem" local cert_dst case $DBMS in MYSQL) common_cnf="$COMMON_MY_CNF" cnf_src="${NODE_MY_CNF[$node]}" cnf_dst="${NODE_TEST_DIR[$node]}/mysql/etc/my.cnf" ca_dst="${NODE_TEST_DIR[$node]}/mysql/var/galera_ca.pem" key_dst="${NODE_TEST_DIR[$node]}/mysql/var/galera_key.pem" cert_dst="${NODE_TEST_DIR[$node]}/mysql/var/galera_cert.pem" ;; PGSQL|*) echo "Unsupported DBMS: '$DBMS'" >&2 return 1 ;; esac if [ -n "$common_cnf" ] || [ -n "$cnf_src" ] then if [ "${NODE_LOCATION[$node]}" = "local" ] then ([ -n "$common_cnf" ] && cat "$common_cnf" && \ [ -n "$cnf_src" ] && cat "$cnf_src") > "$cnf_dst" cat "$ca_src" > "$ca_dst" cat "$key_src" > "$key_dst" cat "$cert_src" > "$cert_dst" else local remote="${NODE_LOCATION[$node]}" ([ -n "$common_cnf" ] && cat "$common_cnf" && \ [ -n "$cnf_src" ] && cat "$cnf_src") | \ ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$remote" "cat > $cnf_dst" cat "$ca_src" | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$remote" "cat > $ca_dst" cat "$key_src" | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$remote" "cat > $key_dst" cat "$cert_src" | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$remote" "cat > $cert_dst" fi fi } copy_file_node() { set -x local -r src_file="$1" local -r dst_file="$2" local -r node="$3" cat "$src_file" | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${NODE_LOCATION[$node]}" "cat - > ${NODE_TEST_DIR[$node]}/$dst_file" } copy_file() { local -r src_file="$1" local -r dst_file="$2" start_jobs copy_file_node "$src_file" "$dst_file" } install_node() { local dist=$1 node_job untar_cmd "$@" } install() { local dist=$1 start_jobs untar_cmd $dist wait_jobs } galera-4-26.4.22/tests/scripts/misc.sh000644 000162 177776 00000001277 14755062442 020641 0ustar00jenkinsnogroup000000 000000 # # Miscellaneous functions # # Sleeps variable amount of seconds (by default 1-10) pause() #min_sleep #var_sleep { local min_sleep=${1:-"1"} local var_sleep=${2:-"10"} local p=$(( $RANDOM % var_sleep + min_sleep )) echo "Sleeping for $p sec." sleep $p } # Pauses given processes (load) to perform consistency check consistency_check() #pids { local ret=0 local pids="$@" [ -n "$pids" ] && kill -STOP $pids sleep 1 check || (sleep 2; check) || (sleep 3; check) || ret=$? [ -n "$pids" ] && kill -CONT $pids # processes will receive SIGHUP in case of script exit return $ret } find_mysqld_pid() { ps ax | grep mysqld | grep -w ^\ *$1 > /dev/null } galera-4-26.4.22/tests/scripts/signal.sh000644 000162 177776 00000000560 14755062442 021155 0ustar00jenkinsnogroup000000 000000 # # Sends signal to process # signal_cmd() { local sig=$1 local node=$2 local dir="${NODE_TEST_DIR[$node]}" echo $sig $node case $DBMS in "MYSQL") echo -n "kill -$sig \$(cat $dir/mysql/var/mysqld.pid)" ;; "PGSQL"|*) echo "Not supported" >&2 return 1 ;; esac } signal_node() { node_job signal_cmd "$@" } galera-4-26.4.22/tests/scripts/jobs.sh000644 000162 177776 00000005302 14755062442 020634 0ustar00jenkinsnogroup000000 000000 # # Routines pertaining to parallel execution # # xxxx_job() functions assume node index as last parameter # # Logging must happen on different layers. # e.g. logging of stderr must happen on the same level as recording of return # code. While stdout log should record only the output of command. #_local_job() #{ # local cmd="$1" # eval "$($@)" # eval $cmd #} #_ssh_job() #{ # local node=${@:$#} # last argument # local cmd="$($@)" # local cmd="$1" # # ssh -ax ${NODE_LOCATION[$node]} "$cmd" #} _date() { echo -n $(date +'%y%m%d %T.%N' | cut -c 1-19) } virtual_job() { local node=${@:$#} # last argument local out="$BASE_OUT/${1}_${NODE_ID[$node]}.out" local cmd="$($@)" if [ "${NODE_LOCATION[$node]}" = "local" ] then # local_job "$cmd" 1>"$out" eval "$cmd" 1>"$out" else # ssh_job "$cmd" 1>"$out" ssh -ax -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no ${NODE_LOCATION[$node]} "$cmd" 1>"$out" fi } # This function tries to add a bit of polymorphism by treating untar cmd # specially. The speciality comes from that it not only excutes command, # but also transfers data # # Usage: node_job cmd opt1 opt2 ... optN node # node_job() { local cmd=$1 shift local node=${@:$#} # last argument local node_id="${NODE_ID[$node]}" local prefix="$BASE_RUN/${cmd}_${node_id}" local rcode=0 local start=$SECONDS case $cmd in "untar_cmd") local dist="$1" shift cat "$dist" | virtual_job "$cmd" "$@" 2>"$prefix.err" && \ copy_config $node 2>"$prefix.err" || rcode=$? ;; *) virtual_job "$cmd" "$@" 2>"$prefix.err" || rcode=$? ;; esac echo $rcode > "$prefix.ret" echo -n "$(_date) Job '$cmd' on '$node_id'" if [ $rcode -eq 0 ] then echo " complete in $(($SECONDS - $start)) seconds, " else echo " failed with code: $rcode, " echo "FAILED COMMAND: $($cmd $@)" echo "REASON: $(cat "$prefix.err")" fi return $rcode } start_jobs() { SECONDS=0 local node for node in $NODE_LIST do local node_id="${NODE_ID[$node]}" local prefix="$BASE_RUN/${1}_$node_id" node_job "$@" $node & echo $! > "$prefix.pid" echo "$(_date) Job '$1' on '$node_id' started" done echo "All jobs started" } wait_jobs() { local err=0 local node for node in $NODE_LIST do wait %% 2>$BASE_RUN/wait.err || err=$?; # 127 - no more jobs if [ $err -eq 127 ]; then err=0; break; fi if [ $err -gt 128 ]; then err=0; fi # ignore signals done echo "$(_date) All jobs complete in $SECONDS seconds" return $err } galera-4-26.4.22/tests/scripts/command.sh000755 000162 177776 00000001377 14755062442 021330 0ustar00jenkinsnogroup000000 000000 #!/bin/bash -eu help() { echo "Usage: $0 [args]" echo "Cluster commands: install, remove, start, stop, check" echo "Node commands: start_node, stop_node, restart_node, check_node," echo " kill_node" echo "Command help: $0 help" } if [ $# -eq 0 ]; then help >&2; exit 1; fi declare -r DIST_BASE=$(cd $(dirname $0)/..; pwd -P) declare -r DIST_SCRIPTS="$DIST_BASE/scripts" # later create config.sh to read config from command line options declare -r TEST_BASE=${TEST_BASE:-"$DIST_BASE"} . "$TEST_BASE/conf/main.conf" . $DIST_SCRIPTS/jobs.sh . $DIST_SCRIPTS/install.sh . $DIST_SCRIPTS/remove.sh . $DIST_SCRIPTS/action.sh . $DIST_SCRIPTS/kill.sh . $DIST_SCRIPTS/signal.sh command=$1 shift $command "$@" galera-4-26.4.22/tests/scripts/README000644 000162 177776 00000003374 14755062442 020232 0ustar00jenkinsnogroup000000 000000 THIS DIRECTORY This directory contains a library of cluster manipulation scripts. The main idea is facilitation of parallel operations and minimizaiton of code duplicaiton. Executable file is command.sh. It sources the configuration and the rest of the scripts which implement different functions: jobs.sh - the main file which defines generic command execution framework. It implements parallel and per-node command execution routines and enables unified logging behaviour. Other scripts just have to define the actual command functions following certain conventions, so far there is only one: node index is appended to the list of command line parameters, then everything is passed to the function. install.sh - implements a cluster-wide 'install' command which takes a name of the distribution file as the first argument remove.sh - implements a cluster-wide "remove" command. action.sh - usual start/stop/restart/check commands, both cluster-wide and per node. kill.sh - a per-node kill -9 command Assumed convention: cluster-wide commands are just commnads, per-node commands have _node suffix. E.g. ./command.sh start starts all nodes, ./command.sh stop_node 1 stops only node number 1. Numbering is 0-based. It is intended that each command should implement its own help. SPECIAL FILES AND DIRECTORIES: ../conf directory contains configuration files. Each command creates at least 4 files named _. where is: out - standard output of the command err - standard error of the command pid - pid of the porcess executing command ret - return code of the command "out" files are placed into $BASE_OUT directory, other files are placed in $BASE_RUN. The reason to separate is unclear. galera-4-26.4.22/tests/scripts/remove.sh000644 000162 177776 00000000341 14755062442 021172 0ustar00jenkinsnogroup000000 000000 # # Routines to remove test distribution from nodes # remove_cmd() { local node=${@:$#} local dirn="${NODE_TEST_DIR[$node]}" echo -n 'rm -rf '"$dirn"'/*' } remove() { start_jobs remove_cmd wait_jobs } galera-4-26.4.22/tests/scripts/kill.sh000644 000162 177776 00000001143 14755062442 020631 0ustar00jenkinsnogroup000000 000000 # # Ungracefully kills the process # kill_cmd() { local node=${@:$#} local dir="${NODE_TEST_DIR[$node]}" case $DBMS in "MYSQL") echo -n "kill -9 \$(cat $dir/mysql/var/mysqld.pid)" ;; "PGSQL"|*) echo "Not supported" >&2 return 1 ;; esac } kill_node() { local node=${@:$#} local dir="${NODE_TEST_DIR[$node]}" local pid=$(cat $dir/mysql/var/mysqld.pid) node_job kill_cmd "$@" # wait process to disappear. while find_mysqld_pid $pid do sleep 0.1 done } kill_all() { start_jobs kill_cmd wait_jobs } galera-4-26.4.22/galera/000755 000162 177776 00000000000 14755062445 015750 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/galera/tests/000755 000162 177776 00000000000 14755062445 017112 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/galera/tests/defaults_check.cpp000644 000162 177776 00000025556 14755062442 022574 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2018-2023 Codership Oy // #include extern "C" int wsrep_loader(wsrep_t*); #include #include #include #include #include // GU_WORDSIZE #include #include #include #include #include #include // unlink() /* "magic" value for parameters which defaults a variable */ static const char* const VARIABLE = "variable"; static const char* Defaults[] = { "base_dir", ".", "base_port", "4567", "cert.log_conflicts", "no", "cert.optimistic_pa", "yes", "debug", "no", #ifdef GU_DBUG_ON "dbug", "", #endif "evs.auto_evict", "0", "evs.causal_keepalive_period", "PT1S", "evs.debug_log_mask", "0x1", "evs.delay_margin", "PT1S", "evs.delayed_keep_period", "PT30S", "evs.inactive_check_period", "PT0.5S", "evs.inactive_timeout", "PT15S", "evs.info_log_mask", "0", "evs.install_timeout", "PT7.5S", "evs.join_retrans_period", "PT1S", "evs.keepalive_period", "PT1S", "evs.max_install_timeouts", "3", "evs.send_window", "4", "evs.stats_report_period", "PT1M", "evs.suspect_timeout", "PT5S", "evs.use_aggregate", "true", "evs.user_send_window", "2", "evs.version", "1", "evs.view_forget_timeout", "P1D", #ifndef NDEBUG "gcache.debug", "0", #endif "gcache.dir", ".", "gcache.keep_pages_size", "0", "gcache.mem_size", "0", "gcache.name", "galera.cache", "gcache.page_size", "128M", "gcache.recover", "yes", "gcache.size", "128M", "gcomm.thread_prio", "", "gcs.fc_debug", "0", "gcs.fc_factor", "1.0", "gcs.fc_limit", "16", "gcs.fc_master_slave", "no", "gcs.fc_single_primary", "no", "gcs.max_packet_size", "64500", "gcs.max_throttle", "0.25", #if (GU_WORDSIZE == 32) "gcs.recv_q_hard_limit", "2147483647", #elif (GU_WORDSIZE == 64) "gcs.recv_q_hard_limit", "9223372036854775807", #endif "gcs.recv_q_soft_limit", "0.25", "gcs.sync_donor", "no", "gmcast.listen_addr", "tcp://0.0.0.0:4567", "gmcast.mcast_addr", "", "gmcast.mcast_ttl", "1", "gmcast.peer_timeout", "PT3S", "gmcast.segment", "0", "gmcast.time_wait", "PT5S", "gmcast.version", "0", // "ist.recv_addr", no default, "pc.announce_timeout", "PT3S", "pc.checksum", "false", "pc.ignore_quorum", "false", "pc.ignore_sb", "false", "pc.linger", "PT20S", "pc.npvo", "false", "pc.recovery", "true", "pc.version", "0", "pc.wait_prim", "true", "pc.wait_prim_timeout", "PT30S", "pc.weight", "1", "protonet.backend", "asio", "protonet.version", "0", "repl.causal_read_timeout", "PT30S", "repl.commit_order", "3", "repl.key_format", "FLAT8", "repl.max_ws_size", "2147483647", "repl.proto_max", "11", #ifdef GU_DBUG_ON "signal", "", #endif "socket.checksum", "2", "socket.recv_buf_size", "auto", "socket.send_buf_size", "auto", // "socket.dynamic", no default, // "socket.ssl", no default, // "socket.ssl_cert", no default, // "socket.ssl_cipher", no default, // "socket.ssl_compression", no default, // "socket.ssl_key", no default, // "socket.ssl_reload" no default, NULL }; typedef std::map DefaultsMap; static void fill_in_expected(DefaultsMap& map, const char* def_list[]) { for (int i(0); def_list[i] != NULL; i += 2) { std::pair param(def_list[i], def_list[i+1]); DefaultsMap::iterator it(map.insert(param).first); ck_assert_msg(it != map.end(), "Failed to insert KV pair: %s = %s", param.first.c_str(), param.second.c_str()); } } static void fill_in_real(DefaultsMap& map, wsrep_t& provider) { std::vector > kv_pairs; char* const opt_string(provider.options_get(&provider)); gu::Config::parse(kv_pairs, opt_string); ::free(opt_string); for (unsigned int i(0); i < kv_pairs.size(); ++i) { std::pair const trimmed(kv_pairs[i].first, kv_pairs[i].second); DefaultsMap::iterator it(map.insert(trimmed).first); ck_assert_msg(it != map.end(), "Failed to insert KV pair: %s = %s", trimmed.first.c_str(), trimmed.second.c_str()); } } static void log_cb(wsrep_log_level_t l, const char* c) { if (l <= WSREP_LOG_ERROR) // only log errors to avoid output clutter { std::cerr << c << '\n'; } } struct app_ctx { gu::Mutex mtx_; gu::Cond cond_; wsrep_t provider_; bool connected_; app_ctx() : mtx_(), cond_(), provider_(), connected_(false) {} }; static enum wsrep_cb_status conn_cb(void* ctx, const wsrep_view_info_t* view) { (void)view; app_ctx* c(static_cast(ctx)); gu::Lock lock(c->mtx_); if (!c->connected_) { c->connected_ = true; c->cond_.broadcast(); } else { assert(0); } return WSREP_CB_SUCCESS; } static enum wsrep_cb_status view_cb(void* app_ctx, void* recv_ctx, const wsrep_view_info_t* view, const char* state, size_t state_len) { /* make compilers happy about unused arguments */ (void)app_ctx; (void)recv_ctx; (void)view; (void)state; (void)state_len; return WSREP_CB_SUCCESS; } static enum wsrep_cb_status synced_cb(void* app_ctx) { (void)app_ctx; return WSREP_CB_SUCCESS; } static void* recv_func(void* ctx) { app_ctx* c(static_cast(ctx)); wsrep_t& provider(c->provider_); wsrep_status_t const ret(provider.recv(&provider, NULL)); ck_assert_msg(WSREP_OK == ret, "recv() returned %d", ret); return NULL; } START_TEST(defaults) { DefaultsMap expected_defaults, real_defaults; fill_in_expected(expected_defaults, Defaults); app_ctx ctx; wsrep_t& provider(ctx.provider_); int ret = wsrep_status_t(wsrep_loader(&provider)); ck_assert(WSREP_OK == ret); struct wsrep_init_args init_args = { &ctx, // void* app_ctx /* Configuration parameters */ NULL, // const char* node_name NULL, // const char* node_address NULL, // const char* node_incoming NULL, // const char* data_dir NULL, // const char* options 0, // int proto_ver /* Application initial state information. */ NULL, // const wsrep_gtid_t* state_id NULL, // const wsrep_buf_t* state /* Application callbacks */ log_cb, // wsrep_log_cb_t logger_cb conn_cb,// wsrep_connected_cb_t connected_cb view_cb,// wsrep_view_cb_t view_handler_cb NULL, // wsrep_sst_request_cb_t sst_request_cb NULL, // wsrep_encrypt_cb_t encrypt_cb /* Applier callbacks */ NULL, // wsrep_apply_cb_t apply_cb NULL, // wsrep_unordered_cb_t unordered_cb /* State Snapshot Transfer callbacks */ NULL, // wsrep_sst_donate_cb_t sst_donate_cb synced_cb,// wsrep_synced_cb_t synced_cb }; ret = provider.init(&provider, &init_args); ck_assert(WSREP_OK == ret); /* some defaults are set only on connection attmept */ ret = provider.connect(&provider, "cluster_name", "gcomm://", "", false); ck_assert_msg(WSREP_OK == ret, "connect() returned %d", ret); fill_in_real(real_defaults, provider); mark_point(); if (WSREP_OK == ret) /* if connect() was a success, need to disconnect() */ { /* some configuration change events need to be received */ gu_thread_t recv_thd; gu_thread_create(&recv_thd, NULL, recv_func, &ctx); mark_point(); /* @todo:there is a race condition in the library when disconnect() is * called right after connect() */ { /* sync with connect callback */ gu::Lock lock(ctx.mtx_); while(!ctx.connected_) lock.wait(ctx.cond_); } mark_point(); ret = provider.disconnect(&provider); ck_assert_msg(WSREP_OK == ret, "disconnect() returned %d", ret); ret = gu_thread_join(recv_thd, NULL); ck_assert_msg(0 == ret, "Could not join thread: %d (%s)", ret, strerror(ret)); } mark_point(); /* cleanup files */ ::unlink(real_defaults.find("gcache.name")->second.c_str()); ::unlink("grastate.dat"); /* now compare expected and real maps */ std::ostringstream err; DefaultsMap::iterator expected(expected_defaults.begin()); while (expected != expected_defaults.end()) { DefaultsMap::iterator real(real_defaults.find(expected->first)); if (real != real_defaults.end()) { if (expected->second != VARIABLE && expected->second != real->second) { err << "Provider default for " << real->first << ": " << real->second << " differs from expected " << expected->second << '\n'; } real_defaults.erase(real); } else { err << "Provider missing " << expected->first <<" parameter\n"; } expected_defaults.erase(expected++); } mark_point(); DefaultsMap::iterator real(real_defaults.begin()); while (real != real_defaults.end()) { err << "Provider has extra parameter: " << real->first << " = " << real->second << '\n'; real_defaults.erase(real++); } ck_assert_msg(err.str().empty(), "Defaults discrepancies detected:\n%s", err.str().c_str()); } END_TEST Suite* defaults_suite() { Suite* s = suite_create("Defaults"); TCase* tc; tc = tcase_create("defaults"); tcase_add_test(tc, defaults); tcase_set_timeout(tc, 120); suite_add_tcase(s, tc); return s; } galera-4-26.4.22/galera/tests/trx_handle_check.cpp000644 000162 177776 00000024770 14755062442 023112 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2020 Codership Oy // #include "trx_handle.hpp" #include #include #include using namespace std; using namespace galera; template void check_states_graph( int graph[TrxHandle::num_states_][TrxHandle::num_states_], T* trx, const std::vector& visits) { // Check that no allowed state transition causes an abort std::vector visited(TrxHandle::num_states_); std::fill(visited.begin(), visited.end(), 0); for (int i(0); i < TrxHandle::num_states_; ++i) { trx->force_state(TrxHandle::State(i)); for (int j(0); j < TrxHandle::num_states_; ++j) { if (graph[i][j]){ log_info << "Checking transition " << trx->state() << " -> " << TrxHandle::State(j); trx->set_state(TrxHandle::State(j)); visited[i] = 1; visited[j] = 1; } else { // TODO: Currently FSM transition calls abort on // unknown transition, figure out how to fix it // to verify also that incorrect transitions cause // proper error. } trx->force_state(TrxHandle::State(i)); } } for (int i(0); i < TrxHandle::num_states_; ++i) { ck_assert_msg(visited[i] == visits[i], "i = %i visited = %i visits = %i", i, visited[i], visits[i]); } } START_TEST(test_states_master) { log_info << "START test_states_master"; TrxHandleMaster::Pool tp(TrxHandleMaster::LOCAL_STORAGE_SIZE(), 16, "test_states_master"); wsrep_uuid_t uuid = {{1, }}; // first check basic stuff // 1) initial state is executing // 2) invalid state changes are caught // 3) valid state changes change state TrxHandleMasterPtr trx(TrxHandleMaster::New(tp, TrxHandleMaster::Defaults, uuid, -1, 1), TrxHandleMasterDeleter()); galera::TrxHandleLock lock(*trx); ck_assert(trx->state() == TrxHandle::S_EXECUTING); // Matrix representing directed graph of TrxHandleMaster transitions, // see galera/src/trx_handle.cpp // EXECUTING 0 // MUST_ABORT 1 // ABORTING 2 // REPLICATING 3 // CERTIFYING 4 // MUST_REPLAY 5 // REPLAYING 6 // APPLYING 7 // COMMITTING 8 // ROLLING_BACK 9 // COMMITTED 10 // ROLLED_BACK 11 int state_trans_master[TrxHandle::num_states_][TrxHandle::num_states_] = { // 0 1 2 3 4 5 6 7 8 9 10 11 To / From { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1 }, // 0 { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 1 { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 }, // 2 { 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, // 3 { 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, // 4 { 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, // 5 { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, // 6 { 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, // 7 { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }, // 8 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // 9 { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 10 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // 11 }; // Visits all states std::vector visits(TrxHandle::num_states_); std::fill(visits.begin(), visits.end(), 1); check_states_graph(state_trans_master, trx.get(), visits); } END_TEST START_TEST(test_states_slave) { log_info << "START test_states_slave"; TrxHandleSlave::Pool sp(sizeof(TrxHandleSlave), 16, "test_states_slave"); int state_trans_slave[TrxHandle::num_states_][TrxHandle::num_states_] = { // 0 1 2 3 4 5 6 7 8 9 10 11 To / From { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0 EXECUTING { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 1 MUST_ABORT { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 2 ABORTING { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, // 3 REPLICATING { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, // 4 CERTIFYING { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 5 MUST_REPLAY { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 6 REPLAYING { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, // 7 APPLYING { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }, // 8 COMMITTNG { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 9 ROLLING_BACK { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 10 COMMITTED { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // 11 ROLLED_BACK }; TrxHandleSlavePtr ts(TrxHandleSlave::New(false, sp), TrxHandleSlaveDeleter()); ck_assert(ts->state() == TrxHandle::S_REPLICATING); std::vector visits(TrxHandle::num_states_); std::fill(visits.begin(), visits.end(), 0); visits[TrxHandle::S_REPLICATING] = 1; visits[TrxHandle::S_CERTIFYING] = 1; visits[TrxHandle::S_APPLYING] = 1; visits[TrxHandle::S_COMMITTING] = 1; visits[TrxHandle::S_COMMITTED] = 1; check_states_graph(state_trans_slave, ts.get(), visits); } END_TEST START_TEST(test_serialization) { TrxHandleMaster::Pool lp(4096, 16, "serialization_lp"); TrxHandleSlave::Pool sp(sizeof(TrxHandleSlave), 16, "serialization_sp"); for (int version = 3; version <= 5; ++version) { galera::TrxHandleMaster::Params const trx_params("", version, KeySet::MAX_VERSION); wsrep_uuid_t uuid; gu_uuid_generate(&uuid, 0, 0); TrxHandleMasterPtr trx (TrxHandleMaster::New(lp, trx_params, uuid, 4567, 8910), TrxHandleMasterDeleter()); std::vector buf; trx->serialize(0, buf); ck_assert(buf.size() > 0); TrxHandleSlavePtr txs1(TrxHandleSlave::New(false, sp), TrxHandleSlaveDeleter()); gcs_action const act = { 1, 2, buf.data(), int(buf.size()), GCS_ACT_WRITESET}; ck_assert(txs1->unserialize(act) > 0); ck_assert(txs1->global_seqno() == act.seqno_g); ck_assert(txs1->local_seqno() == act.seqno_l); } } END_TEST static enum wsrep_cb_status apply_cb( void* ctx, const wsrep_ws_handle_t* wh, uint32_t flags, const wsrep_buf_t* data, const wsrep_trx_meta_t* meta, wsrep_bool_t* exit_loop ) { std::vector* const res(static_cast* >(ctx)); ck_assert(NULL != res); const char* const c(static_cast(data->ptr)); ck_assert(NULL != c); ck_assert(1 == data->len); res->push_back(*c); return WSREP_CB_SUCCESS; } START_TEST(test_streaming) { TrxHandleMaster::Pool lp(4096, 16, "streaming_lp"); TrxHandleSlave::Pool sp(sizeof(TrxHandleSlave), 16, "streaming_sp"); int const version(galera::WriteSetNG::VER5); galera::TrxHandleMaster::Params const trx_params("", version, KeySet::MAX_VERSION); wsrep_uuid_t uuid; gu_uuid_generate(&uuid, 0, 0); TrxHandleMasterPtr trx(TrxHandleMaster::New(lp, trx_params, uuid, 4567,8910), TrxHandleMasterDeleter()); galera::TrxHandleLock lock(*trx); std::vector src(3); // initial wirteset src[0] = 'a'; src[1] = 'b'; src[2] = 'c'; std::vector res; // apply_cb should reproduce src in res ck_assert(src != res); ck_assert(trx->flags() & TrxHandle::F_BEGIN); { // 0. first fragment A trx->append_data(&src[0], 1, WSREP_DATA_ORDERED, false); trx->finalize(0); std::vector buf; trx->serialize(0, buf); ck_assert(buf.size() > 0); trx->release_write_set_out(); TrxHandleSlavePtr ts(TrxHandleSlave::New(false, sp), TrxHandleSlaveDeleter()); gcs_action const act = { 1, 2, buf.data(), int(buf.size()), GCS_ACT_WRITESET}; ck_assert(ts->unserialize(act) > 0); ck_assert(ts->flags() & TrxHandle::F_BEGIN); ck_assert(!(ts->flags() & TrxHandle::F_COMMIT)); trx->add_replicated(ts); wsrep_bool_t exit_loop; ts->apply(&res, apply_cb, wsrep_trx_meta_t(), exit_loop); } { // 1. middle fragment B trx->append_data(&src[1], 1, WSREP_DATA_ORDERED, false); trx->finalize(1); std::vector buf; trx->serialize(0, buf); ck_assert(buf.size() > 0); trx->release_write_set_out(); TrxHandleSlavePtr ts(TrxHandleSlave::New(false, sp), TrxHandleSlaveDeleter()); gcs_action const act = { 2, 3, buf.data(), int(buf.size()), GCS_ACT_WRITESET}; ck_assert(ts->unserialize(act) > 0); ck_assert(!(ts->flags() & TrxHandle::F_BEGIN)); ck_assert(!(ts->flags() & TrxHandle::F_COMMIT)); trx->add_replicated(ts); wsrep_bool_t exit_loop; ts->apply(&res, apply_cb, wsrep_trx_meta_t(), exit_loop); } { // 2. last fragment C trx->append_data(&src[2], 1, WSREP_DATA_ORDERED, false); trx->set_flags(TrxHandle::F_COMMIT); // commit trx->finalize(2); std::vector buf; trx->serialize(0, buf); ck_assert(buf.size() > 0); trx->release_write_set_out(); TrxHandleSlavePtr ts(TrxHandleSlave::New(false, sp), TrxHandleSlaveDeleter()); gcs_action const act = { 3, 4, buf.data(), int(buf.size()), GCS_ACT_WRITESET}; ck_assert(ts->unserialize(act) > 0); ck_assert(!(ts->flags() & TrxHandle::F_BEGIN)); ck_assert(ts->flags() & TrxHandle::F_COMMIT); trx->add_replicated(ts); wsrep_bool_t exit_loop; ts->apply(&res, apply_cb, wsrep_trx_meta_t(), exit_loop); } ck_assert(res == src); } END_TEST Suite* trx_handle_suite() { Suite* s = suite_create("trx_handle"); TCase* tc; tc = tcase_create("test_states_master"); tcase_add_test(tc, test_states_master); suite_add_tcase(s, tc); tc = tcase_create("test_states_slave"); tcase_add_test(tc, test_states_slave); suite_add_tcase(s, tc); tc = tcase_create("test_serialization"); tcase_add_test(tc, test_serialization); suite_add_tcase(s, tc); tc = tcase_create("test_streaming"); tcase_add_test(tc, test_streaming); suite_add_tcase(s, tc); return s; } galera-4-26.4.22/galera/tests/data_set_check.cpp000644 000162 177776 00000017721 14755062442 022544 0ustar00jenkinsnogroup000000 000000 /* Copyright (C) 2013-2023 Codership Oy * * $Id$ */ #undef NDEBUG #include "../src/data_set.hpp" #include "gu_logger.hpp" #include "gu_hexdump.hpp" #include using namespace galera; class TestBaseName : public gu::Allocator::BaseName { std::string str_; public: TestBaseName(const char* name) : str_(name) {} void print(std::ostream& os) const { os << str_; } }; class TestRecord { public: TestRecord (size_t size, const char* str) : size_(size), buf_(reinterpret_cast(::malloc(size_))), str_(reinterpret_cast(buf_) + sizeof(uint32_t)), own_(true) { if (0 == buf_) throw std::runtime_error("failed to allocate record"); void* tmp = const_cast(buf_); *reinterpret_cast(tmp) = htog32(size_); ::strncpy (const_cast(str_), str, size_ - 4); } TestRecord (const void* const buf, ssize_t const size) : size_(TestRecord::serial_size(buf, size)), buf_(buf), str_(reinterpret_cast(buf_) + sizeof(uint32_t)), own_(false) {} TestRecord (const TestRecord& t) : size_(t.size_), buf_(t.buf_), str_(t.str_), own_(false) {} virtual ~TestRecord () { if (own_) free (const_cast(buf_)); } const void* buf() const { return buf_; } const char* c_str() const { return str_; } ssize_t serial_size() const { return my_serial_size(); } static ssize_t serial_size(const void* const buf, ssize_t const size) { check_buf (buf, size, 1); return gtoh32 (*reinterpret_cast(buf)); } bool operator!= (const TestRecord& t) const { return (size_ != t.size_ || ::memcmp(buf_, t.buf_, size_)); } bool operator== (const TestRecord& t) const { return (!(*this != t)); } private: size_t const size_; const void* const buf_; const char* const str_; bool const own_; ssize_t my_serial_size () const { return size_; }; ssize_t my_serialize_to (void* buf, ssize_t size) const { check_buf (buf, size, size_); ::memcpy (buf, buf_, size_); return size_; } static void check_buf (const void* const buf, ssize_t const size, ssize_t min_size) { if (gu_unlikely (buf == 0 || size < min_size)) throw std::length_error("buffer too short"); } TestRecord& operator= (const TestRecord&); }; static void test_ver(gu::RecordSet::Version const rsv) { int const alignment (rsv >= gu::RecordSet::VER2 ? gu::RecordSet::VER2_ALIGNMENT : 1); size_t const MB = 1 << 20; TestRecord rout0(128, "abc0"); TestRecord rout1(127, "abc1"); TestRecord rout2(126, "012345"); TestRecord rout3(125, "defghij"); TestRecord rout4(3*MB, "klm"); TestRecord rout5(1*MB, "qpr"); std::vector records; records.push_back (&rout0); records.push_back (&rout1); records.push_back (&rout2); records.push_back (&rout3); records.push_back (&rout4); records.push_back (&rout5); union { gu::byte_t buf[1024]; gu_word_t align; } reserved; TestBaseName str("data_set_test"); DataSetOut dset_out(reserved.buf, sizeof(reserved.buf), str, DataSet::VER1, rsv); size_t offset(dset_out.size()); // this should be allocated inside current page offset += dset_out.append (rout0.buf(), rout0.serial_size(), true); ck_assert_msg(dset_out.size() == offset, "expected: %zu, got %zu", offset, dset_out.size()); // this should trigger new page since not stored offset += dset_out.append (rout1.buf(), rout1.serial_size(), false); ck_assert(dset_out.size() == offset); // this should trigger new page since previous one was not stored offset += dset_out.append (rout2.buf(), rout2.serial_size(), true); ck_assert(dset_out.size() == offset); // this should trigger a new page, since not stored offset += dset_out.append (rout3.buf(), rout3.serial_size(), false); ck_assert(dset_out.size() == offset); // this should trigger new page, because won't fit in the current page offset += dset_out.append (rout4.buf(), rout4.serial_size(), true); ck_assert(dset_out.size() == offset); // this should trigger new page, because 4MB RAM limit exceeded offset += dset_out.append (rout5.buf(), rout5.serial_size(), false); ck_assert(dset_out.size() == offset); ck_assert(1 == size_t(dset_out.count())); DataSetOut::GatherVector out_bufs; out_bufs().reserve (dset_out.page_count()); bool const padding_page(offset % alignment); size_t min_out_size(0); for (size_t i = 0; i < records.size(); ++i) { min_out_size += records[i]->serial_size(); } size_t const out_size (dset_out.gather (out_bufs)); ck_assert_msg(0 == out_size % alignment, "out size %zu not aligned by %d", out_size, alignment); ck_assert(out_size > min_out_size && out_size <= offset); ck_assert_msg(out_bufs->size() <= size_t(dset_out.page_count()) && out_bufs->size() >= size_t(dset_out.page_count() - padding_page), "Expected %zu buffers, got: %zd", dset_out.page_count(), out_bufs->size()); /* concatenate all buffers into one */ std::vector in_buf; in_buf.reserve(out_size); mark_point(); for (size_t i = 0; i < out_bufs->size(); ++i) { ck_assert(0 != out_bufs[i].ptr); log_info << "\nadding buf " << i << ": " << gu::Hexdump(out_bufs[i].ptr, std::min(out_bufs[i].size, 24), true); size_t old_size = in_buf.size(); const gu::byte_t* ptr (reinterpret_cast(out_bufs[i].ptr)); in_buf.insert (in_buf.end(), ptr, ptr + out_bufs[i].size); ck_assert(old_size + out_bufs[i].size == in_buf.size()); } ck_assert_msg(in_buf.size() == out_size, "Sent buf size: %zu, recvd buf size: %zu", out_size, in_buf.size()); log_info << "Resulting DataSet buffer:\n" << gu::Hexdump(in_buf.data(), 32, false) << '\n' << gu::Hexdump(in_buf.data(), 32, true); galera::DataSetIn const dset_in(dset_out.version(), in_buf.data(), in_buf.size()); ck_assert(dset_in.size() == dset_out.size()); ck_assert(dset_in.count() == dset_out.count()); try { dset_in.checksum(); } catch(gu::Exception& e) { ck_abort_msg("%s", e.what()); } for (ssize_t i = 0; i < dset_in.count(); ++i) { gu::Buf data = dset_in.next(); TestRecord const rin(data.ptr, data.size); ck_assert_msg(rin == *records[i], "Record %zd failed: expected %s, found %s", i, records[i]->c_str(), rin.c_str()); } galera::DataSetIn dset_in_empty; dset_in_empty.init(dset_out.version(), in_buf.data(), in_buf.size()); ck_assert(dset_in_empty.size() == dset_out.size()); ck_assert(dset_in_empty.count() == dset_out.count()); for (ssize_t i = 0; i < dset_in_empty.count(); ++i) { gu::Buf data = dset_in_empty.next(); TestRecord const rin(data.ptr, data.size); ck_assert_msg(rin == *records[i], "Record %zd failed: expected %s, found %s", i, records[i]->c_str(), rin.c_str()); } } #ifndef GALERA_ONLY_ALIGNED START_TEST (ver1) { test_ver(gu::RecordSet::VER1); } END_TEST #endif /* GALERA_ONLY_ALIGNED */ START_TEST (ver2) { test_ver(gu::RecordSet::VER2); } END_TEST Suite* data_set_suite () { TCase* t = tcase_create ("DataSet"); #ifndef GALERA_ONLY_ALIGNED tcase_add_test (t, ver1); #endif tcase_add_test (t, ver2); tcase_set_timeout(t, 60); Suite* s = suite_create ("DataSet"); suite_add_tcase (s, t); return s; } galera-4-26.4.22/galera/tests/key_set_check.cpp000644 000162 177776 00000064162 14755062442 022424 0ustar00jenkinsnogroup000000 000000 /* copyright (C) 2013-2024 Codership Oy * * $Id$ */ #undef NDEBUG #include "test_key.hpp" #include "../src/key_set.hpp" #include "../src/write_set_ng.hpp" #include "gu_logger.hpp" #include "gu_hexdump.hpp" #include using namespace galera; class TestBaseName : public gu::Allocator::BaseName { std::string str_; public: TestBaseName(const char* name) : str_(name) {} void print(std::ostream& os) const { os << str_; } }; static size_t version_to_hash_size (KeySet::Version const ver) { switch (ver) { case KeySet::FLAT16: ck_abort_msg("FLAT16 is not supported by test"); case KeySet::FLAT16A: return 16; case KeySet::FLAT8: ck_abort_msg( "FLAT8 is not supported by test"); case KeySet::FLAT8A: return 8; default: ck_abort_msg("Unsupported KeySet verison: %d", ver); } abort(); } static void test_ver(gu::RecordSet::Version const rsv, int const ws_ver) { int const alignment (rsv >= gu::RecordSet::VER2 ? gu::RecordSet::VER2_ALIGNMENT : 1); KeySet::Version const tk_ver(KeySet::FLAT16A); size_t const base_size(version_to_hash_size(tk_ver)); union { gu::byte_t buf[1024]; gu_word_t align; } reserved; assert((uintptr_t(reserved.buf) % GU_WORD_BYTES) == 0); TestBaseName const str("key_set_test"); KeySetOut kso (reserved.buf, sizeof(reserved.buf), str, tk_ver, rsv, ws_ver); ck_assert(kso.count() == 0); size_t total_size(kso.size()); log_info << "Start size: " << total_size; TestKey tk0(tk_ver, WSREP_KEY_SHARED, false, "a0"); kso.append(tk0()); ck_assert(kso.count() == 1); total_size += base_size + 2 + 1*4; total_size = GU_ALIGN(total_size, alignment); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); kso.append(tk0()); ck_assert(kso.count() == 1); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); TestKey tk1(tk_ver, WSREP_KEY_SHARED, true, "a0", "a1", "a2"); mark_point(); kso.append(tk1()); int expected_count(3); ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); total_size += base_size + 2 + 2*4; total_size = GU_ALIGN(total_size, alignment); total_size += base_size + 2 + 3*4; total_size = GU_ALIGN(total_size, alignment); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); TestKey tk2(tk_ver, WSREP_KEY_EXCLUSIVE, false, "a0", "a1", "b2"); kso.append(tk2()); expected_count += (ws_ver > 3); ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); if (expected_count == 4) { total_size += base_size + 2 + 3*4; total_size = GU_ALIGN(total_size, alignment); } ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); /* this should update a sronger version of "a2" */ TestKey tk2_(tk_ver, WSREP_KEY_UPDATE, false, "a0", "a1", "a2"); kso.append(tk2_()); expected_count += 1; ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); total_size += base_size + 2 + 3*4; total_size = GU_ALIGN(total_size, alignment); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); /* it is a duplicate, but it should add an exclusive verision of the key */ TestKey tk3(tk_ver, WSREP_KEY_EXCLUSIVE, true, "a0", "a1"); log_info << "######## Appending exclusive duplicate tk3: begin"; kso.append(tk3()); expected_count += (ws_ver <= 3 ? 0 : 1); log_info << "######## Appending exclusive duplicate tk3: end"; ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); if (ws_ver > 3) { total_size += base_size + 2 + 2*4; total_size = GU_ALIGN(total_size, alignment); } ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); /* tk3 should make it impossible to add anything past a0:a1 */ TestKey tk4(tk_ver, WSREP_KEY_EXCLUSIVE, false, "a0", "a1", "c2"); log_info << "######## Appending exclusive duplicate tk4: begin"; kso.append(tk4()); log_info << "######## Appending exclusive duplicate tk4: end"; ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); /* adding shared key should have no effect */ TestKey tk5(tk_ver, WSREP_KEY_SHARED, true, "a0", "a1"); kso.append(tk5()); ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); /* adding REFERENCE key should have no effect */ TestKey tk5_1(tk_ver, WSREP_KEY_REFERENCE, true, "a0", "a1"); kso.append(tk5_1()); ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); /* adding UPDATE key should have no effect */ TestKey tk5_2(tk_ver, WSREP_KEY_UPDATE, true, "a0", "a1"); kso.append(tk5_2()); ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); /* tk5 should not make any changes */ TestKey tk6(tk_ver, WSREP_KEY_EXCLUSIVE, false, "a0", "a1", "c2"); kso.append(tk6()); ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); /* a0:b1:... should still be possible, should add 2 keys: b1 and c2 */ TestKey tk7(tk_ver, WSREP_KEY_REFERENCE, true, "a0", "b1", "c2"); kso.append(tk7()); expected_count += 2; ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); total_size += base_size + 2 + 2*4; total_size = GU_ALIGN(total_size, alignment); total_size += base_size + 2 + 3*4; total_size = GU_ALIGN(total_size, alignment); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); /* make sure a0:b1:b2 is possible despite we have a0:a1:b2 already * (should be no collision on b2) */ TestKey tk8(tk_ver, WSREP_KEY_REFERENCE, false, "a0", "b1", "b2"); kso.append(tk8()); expected_count += (ws_ver > 3); ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); if (ws_ver > 3) { total_size += base_size + 2 + 3*4; total_size = GU_ALIGN(total_size, alignment); } ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); TestKey tk8_1(tk_ver, WSREP_KEY_UPDATE, false, "a0", "b1", "b2"); kso.append(tk8_1()); if (3 == ws_ver || 4 == ws_ver) { /* versions 3, 4 do not distinguish REEFERENCE and UPDATE, the key should be ignored */ } else if (5 <= ws_ver) { /* in version 5 UPDATE is a stronger key than REFERENCE - should be * added to the set */ expected_count++; total_size += base_size + 2 + 3*4; total_size = GU_ALIGN(total_size, alignment); } else abort(); ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); TestKey tk8_2(tk_ver, WSREP_KEY_EXCLUSIVE, false, "a0", "b1", "b2"); kso.append(tk8_2()); if (3 == ws_ver) { /* version 3 does not distinguish REFERENCE, UPDATE and EXCLUSIVE, the key should be ignored */ } else if (4 <= ws_ver) { /* in version 4 EXCLUSIVE is a stronger key than REFERENCE and * in version 5 EXCLUSIVE is a stronger key than UPDATE - should be * added to the set */ expected_count++; total_size += base_size + 2 + 3*4; total_size = GU_ALIGN(total_size, alignment); } else abort(); ck_assert_msg(kso.count() == expected_count,"key count: expected %d, got %d", expected_count, kso.count()); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); TestKey tk8_3(tk_ver, WSREP_KEY_UPDATE, false, "a0", "b1", "b2"); kso.append(tk8_3()); /* UPDATE key is weaker than EXCLUSIVE, should be ignored */ ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); log_info << "size before huge key: " << total_size; char huge_key[2048]; memset (huge_key, 'x', sizeof(huge_key)); huge_key[ sizeof(huge_key) - 1 ] = 0; TestKey tk9(tk_ver, WSREP_KEY_EXCLUSIVE, false, huge_key, huge_key,huge_key); kso.append(tk9()); expected_count += 3; ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); total_size += base_size + 2 + 1*256; total_size = GU_ALIGN(total_size, alignment); total_size += base_size + 2 + 2*256; total_size = GU_ALIGN(total_size, alignment); total_size += base_size + 2 + 3*256; total_size = GU_ALIGN(total_size, alignment); ck_assert_msg(total_size == kso.size(), "Size: %zu, expected: %zu", kso.size(), total_size); log_info << "End size: " << kso.size(); // Verify that SHARED keys are added as a first leaf bunt not over REFERENCE TestKey tk10_ref1(tk_ver, WSREP_KEY_REFERENCE, true, "s0"); kso.append(tk10_ref1()); expected_count += 1; ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); // Should add SHARED even though s1 weaker than s0 (for ver > 3) TestKey tk10_sh(tk_ver, WSREP_KEY_SHARED, true, "s0", "s1"); kso.append(tk10_sh()); expected_count += (ws_ver > 3); // at ver<=3 REF is considered EXC assert(kso.count() == expected_count); ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); TestKey tk10_ref2(tk_ver, WSREP_KEY_REFERENCE, true, "s0", "s1"); kso.append(tk10_ref2()); expected_count += (ws_ver > 3); assert(kso.count() == expected_count); ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); // Try same SHARED once again, should not add anything kso.append(tk10_sh()); ck_assert_msg(kso.count() == expected_count, "key count: expected %d, got %d", expected_count, kso.count()); KeySetOut::GatherVector out; out->reserve(kso.page_count()); size_t const out_size(kso.gather(out)); log_info << "Gather size: " << out_size << ", buf count: " << out->size(); ck_assert_msg(0 == out_size % alignment, "out size not aligned by %zd", out_size % alignment); std::vector in; in.reserve(out_size); for (size_t i(0); i < out->size(); ++i) { const gu::byte_t* ptr(reinterpret_cast(out[i].ptr)); in.insert (in.end(), ptr, ptr + out[i].size); } ck_assert(in.size() == out_size); KeySetIn ksi (kso.version(), in.data(), in.size()); ck_assert_msg(ksi.count() == kso.count(), "Received keys: %d, expected: %d", ksi.count(), kso.count()); ck_assert_msg(ksi.size() == kso.size(), "Received size: %zu, expected: %zu", ksi.size(), kso.size()); try { ksi.checksum(); } catch (std::exception& e) { ck_abort_msg("%s", e.what()); } int branch(0); // to stiffle clang complaints about unused variables int const P_BRANCH(KeySet::KeyPart::prefix(KeyData::BRANCH_KEY_TYPE,ws_ver)); for (int i(0); i < ksi.count(); ++i) { KeySet::KeyPart kp(ksi.next()); branch += (kp.prefix() == P_BRANCH); } KeySetIn ksi_empty; ck_assert_msg(ksi_empty.count() == 0, "Received keys: %d, expected: %d", ksi_empty.count(), 0); ck_assert_msg(ksi_empty.size() == 0, "Received size: %zu, expected: %d", ksi_empty.size(), 0); ksi_empty.init (kso.version(), in.data(), in.size()); ck_assert_msg(ksi_empty.count() == kso.count(), "Received keys: %d, expected: %d", ksi_empty.count(),kso.count()); ck_assert_msg(ksi_empty.size() == kso.size(), "Received size: %zu, expected: %zu", ksi_empty.size(), kso.size()); try { ksi_empty.checksum(); } catch (std::exception& e) { ck_abort_msg("%s", e.what()); } for (int i(0); i < ksi_empty.count(); ++i) { KeySet::KeyPart kp(ksi_empty.next()); branch += (kp.prefix() == P_BRANCH); } ksi_empty.rewind(); for (int i(0); i < ksi_empty.count(); ++i) { KeySet::KeyPart kp(ksi_empty.next()); branch += (kp.prefix() == P_BRANCH); } ck_assert(0 != branch); } #ifndef GALERA_ONLY_ALIGNED START_TEST (ver1_3) { test_ver(gu::RecordSet::VER1, 3); } END_TEST #endif /* GALERA_ONLY_ALIGNED */ START_TEST (ver2_3) { test_ver(gu::RecordSet::VER2, 3); } END_TEST START_TEST (ver2_4) { test_ver(gu::RecordSet::VER2, 4); } END_TEST START_TEST (ver2_5) { test_ver(gu::RecordSet::VER2, 5); } END_TEST struct KsoFixture { union Res { gu::byte_t buf[1024]; gu_word_t align; }; Res res{}; TestBaseName basename{ "ksof" }; galera::KeySetOut kso{ res.buf, sizeof(res.buf), basename, galera::KeySet::FLAT8A, gu::RecordSet::VER2, galera::WriteSetNG::MAX_VERSION }; void append(const std::vector key, wsrep_key_type_t type) { TestKey k{ galera::KeySet::FLAT8A, type, key }; kso.append(k()); } }; /* * Shared leaf */ START_TEST(kso_append_shared_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_shared_over_shared_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_SHARED); f.append({ "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_reference_over_shared_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_SHARED); f.append({ "b", "l" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_update_over_shared_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_SHARED); f.append({ "b", "l" }, WSREP_KEY_UPDATE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_exclusive_over_shared_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_SHARED); f.append({ "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_shared_branch_over_shared_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_SHARED); f.append({"b", "b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_reference_branch_over_shared_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_SHARED); f.append({"b", "b", "l"}, WSREP_KEY_REFERENCE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_update_branch_over_shared_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_SHARED); f.append({"b", "b", "l"}, WSREP_KEY_UPDATE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_exclusive_branch_over_shared_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_SHARED); f.append({"b", "b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_shared_leaf_over_branch) { KsoFixture f; f.append({"b", "l"}, WSREP_KEY_SHARED); f.append({"b"}, WSREP_KEY_SHARED); ck_assert_int_eq(f.kso.count(), 2); } END_TEST /* * Reference leaf */ START_TEST(kso_append_reference_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_shared_over_reference_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_REFERENCE); f.append({ "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_reference_over_reference_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_REFERENCE); f.append({ "b", "l" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_update_over_reference_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_REFERENCE); f.append({ "b", "l" }, WSREP_KEY_UPDATE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_exclusive_over_reference_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_REFERENCE); f.append({ "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_shared_branch_over_reference_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_REFERENCE); f.append({"b", "b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_reference_branch_over_reference_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_REFERENCE); f.append({"b", "b", "l"}, WSREP_KEY_REFERENCE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_update_branch_over_reference_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_REFERENCE); f.append({"b", "b", "l"}, WSREP_KEY_UPDATE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_exclusive_branch_over_reference_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_REFERENCE); f.append({"b", "b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_reference_leaf_over_branch) { KsoFixture f; f.append({"b", "l"}, WSREP_KEY_SHARED); f.append({"b"}, WSREP_KEY_REFERENCE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST /* * Update leaf */ START_TEST(kso_append_update_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_UPDATE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_shared_over_update_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_UPDATE); f.append({ "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_reference_over_update_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_UPDATE); f.append({ "b", "l" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_update_over_update_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_UPDATE); f.append({ "b", "l" }, WSREP_KEY_UPDATE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_exclusive_over_update_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_UPDATE); f.append({ "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_shared_branch_over_update_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_UPDATE); f.append({"b", "b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_reference_branch_over_update_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_UPDATE); f.append({"b", "b", "l"}, WSREP_KEY_REFERENCE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_update_branch_over_update_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_UPDATE); f.append({"b", "b", "l"}, WSREP_KEY_UPDATE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_exclusive_branch_over_update_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_UPDATE); f.append({"b", "b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST START_TEST(kso_append_update_leaf_over_branch) { KsoFixture f; f.append({"b", "l"}, WSREP_KEY_SHARED); f.append({"b"}, WSREP_KEY_UPDATE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST /* * Exclusive leaf */ START_TEST(kso_append_exclusive_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_shared_over_exclusive_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_EXCLUSIVE); f.append({ "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_reference_over_exclusive_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_EXCLUSIVE); f.append({ "b", "l" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_update_over_exclusive_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_EXCLUSIVE); f.append({ "b", "l" }, WSREP_KEY_UPDATE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_exclusive_over_exclusive_leaf) { KsoFixture f; f.append({ "b", "l" }, WSREP_KEY_EXCLUSIVE); f.append({ "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_shared_branch_over_exclusive_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_EXCLUSIVE); f.append({"b", "b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_reference_branch_over_exclusive_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_EXCLUSIVE); f.append({"b", "b", "l"}, WSREP_KEY_REFERENCE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_update_branch_over_exclusive_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_EXCLUSIVE); f.append({"b", "b", "l"}, WSREP_KEY_UPDATE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_exclusive_branch_over_exclusive_leaf) { KsoFixture f; f.append({"b", "b"}, WSREP_KEY_EXCLUSIVE); f.append({"b", "b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(f.kso.count(), 2); } END_TEST START_TEST(kso_append_exclusive_leaf_over_branch) { KsoFixture f; f.append({"b", "l"}, WSREP_KEY_SHARED); f.append({"b"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(f.kso.count(), 3); } END_TEST Suite* key_set_suite () { TCase* t = tcase_create ("KeySet"); #ifndef GALERA_ONLY_ALIGNED tcase_add_test (t, ver1_3); #endif tcase_add_test (t, ver2_3); tcase_add_test (t, ver2_4); tcase_add_test (t, ver2_5); tcase_set_timeout(t, 60); tcase_add_test(t, kso_append_shared_leaf); tcase_add_test(t, kso_append_shared_over_shared_leaf); tcase_add_test(t, kso_append_reference_over_shared_leaf); tcase_add_test(t, kso_append_update_over_shared_leaf); tcase_add_test(t, kso_append_exclusive_over_shared_leaf); tcase_add_test(t, kso_append_shared_branch_over_shared_leaf); tcase_add_test(t, kso_append_reference_branch_over_shared_leaf); tcase_add_test(t, kso_append_update_branch_over_shared_leaf); tcase_add_test(t, kso_append_exclusive_branch_over_shared_leaf); tcase_add_test(t, kso_append_shared_leaf_over_branch); tcase_add_test(t, kso_append_reference_leaf); tcase_add_test(t, kso_append_shared_over_reference_leaf); tcase_add_test(t, kso_append_reference_over_reference_leaf); tcase_add_test(t, kso_append_update_over_reference_leaf); tcase_add_test(t, kso_append_exclusive_over_reference_leaf); tcase_add_test(t, kso_append_shared_branch_over_reference_leaf); tcase_add_test(t, kso_append_reference_branch_over_reference_leaf); tcase_add_test(t, kso_append_update_branch_over_reference_leaf); tcase_add_test(t, kso_append_exclusive_branch_over_reference_leaf); tcase_add_test(t, kso_append_reference_leaf_over_branch); tcase_add_test(t, kso_append_update_leaf); tcase_add_test(t, kso_append_shared_over_update_leaf); tcase_add_test(t, kso_append_reference_over_update_leaf); tcase_add_test(t, kso_append_update_over_update_leaf); tcase_add_test(t, kso_append_exclusive_over_update_leaf); tcase_add_test(t, kso_append_shared_branch_over_update_leaf); tcase_add_test(t, kso_append_reference_branch_over_update_leaf); tcase_add_test(t, kso_append_update_branch_over_update_leaf); tcase_add_test(t, kso_append_exclusive_branch_over_update_leaf); tcase_add_test(t, kso_append_update_leaf_over_branch); tcase_add_test(t, kso_append_exclusive_leaf); tcase_add_test(t, kso_append_shared_over_exclusive_leaf); tcase_add_test(t, kso_append_reference_over_exclusive_leaf); tcase_add_test(t, kso_append_update_over_exclusive_leaf); tcase_add_test(t, kso_append_exclusive_over_exclusive_leaf); tcase_add_test(t, kso_append_shared_branch_over_exclusive_leaf); tcase_add_test(t, kso_append_reference_branch_over_exclusive_leaf); tcase_add_test(t, kso_append_update_branch_over_exclusive_leaf); tcase_add_test(t, kso_append_exclusive_branch_over_exclusive_leaf); tcase_add_test(t, kso_append_exclusive_leaf_over_branch); Suite* s = suite_create ("KeySet"); suite_add_tcase (s, t); return s; } galera-4-26.4.22/galera/tests/saved_state_check.cpp000644 000162 177776 00000011707 14755062442 023260 0ustar00jenkinsnogroup000000 000000 /* * Copyright (C) 2012-2020 Codership Oy */ #include "../src/saved_state.hpp" #include #include "gu_inttypes.hpp" #include #include #include static volatile bool stop(false); using namespace galera; static void* thread_routine (void* arg) { SavedState* st(static_cast(arg)); do { st->mark_unsafe(); st->mark_safe(); } while (!stop); return NULL; } static const int max_threads(16); static gu_thread_t threads[max_threads]; #if defined(GALERA_WITH_VALGRIND) static const int iterations(10); #else static const int iterations(100); #endif // GALERA_WITH_VALGRIND static void start_threads(void* arg) { stop = false; for (int ret = 0; ret < max_threads; ++ret) { gu_thread_t t; int err = gu_thread_create (&t, NULL, thread_routine, arg); ck_assert_msg(0 == err, "Failed to start thread %d: %d (%s)", ret, err, strerror(err)); threads[ret] = t; } } static void stop_threads() { stop = true; for (int t = 0; t < max_threads; ++t) { gu_thread_join(threads[t], NULL); } } static const char* fname("grastate.dat"); START_TEST(test_basic) { unlink (fname); wsrep_uuid_t uuid; wsrep_seqno_t seqno; bool safe_to_bootstrap; { SavedState st(fname); st.get(uuid, seqno, safe_to_bootstrap); ck_assert(uuid == WSREP_UUID_UNDEFINED); ck_assert(seqno == WSREP_SEQNO_UNDEFINED); ck_assert(safe_to_bootstrap == true); gu_uuid_from_string("b2c01654-8dfe-11e1-0800-a834d641cfb5", uuid); seqno = 2345234LL; st.set(uuid, seqno, false); } { SavedState st(fname); wsrep_uuid_t u; wsrep_seqno_t s; bool stb; st.get(u, s, stb); ck_assert(u == uuid); ck_assert(s == seqno); ck_assert(stb == false); } } END_TEST #define TEST_USLEEP 2500 // 2.5ms START_TEST(test_unsafe) { SavedState st(fname); wsrep_uuid_t uuid; wsrep_seqno_t seqno; bool safe_to_bootstrap; st.get(uuid, seqno, safe_to_bootstrap); ck_assert(uuid != WSREP_UUID_UNDEFINED); ck_assert(seqno != WSREP_SEQNO_UNDEFINED); ck_assert(safe_to_bootstrap != true); st.set(uuid, WSREP_SEQNO_UNDEFINED, false); for (int i = 0; i < iterations; ++i) { start_threads(&st); mark_point(); usleep (TEST_USLEEP); st.set(uuid, i, false); // make sure that state is not lost if set concurrently mark_point(); usleep (TEST_USLEEP); stop_threads(); mark_point(); st.get(uuid, seqno, safe_to_bootstrap); ck_assert(uuid != WSREP_UUID_UNDEFINED); ck_assert(seqno == i); ck_assert(safe_to_bootstrap == false); } long marks, locks, writes; st.stats(marks, locks, writes); log_info << "Total marks: " << marks << ", total writes: " << writes << ", total locks: " << locks << "\nlocks ratio: " << (double(locks)/marks) << "\nwrites ratio: " << (double(writes)/locks); } END_TEST START_TEST(test_corrupt) { wsrep_uuid_t uuid; wsrep_seqno_t seqno; bool safe_to_bootstrap; { SavedState st(fname); st.get(uuid, seqno, safe_to_bootstrap); ck_assert(uuid != WSREP_UUID_UNDEFINED); ck_assert(seqno != WSREP_SEQNO_UNDEFINED); ck_assert(safe_to_bootstrap != true); st.set(uuid, WSREP_SEQNO_UNDEFINED, false); } long marks(0), locks(0), writes(0); for (int i = 0; i < iterations; ++i) { SavedState st(fname); // explicitly overwrite corruption mark. st.set (uuid, seqno, false); start_threads(&st); mark_point(); usleep (TEST_USLEEP); st.mark_corrupt(); st.set (uuid, seqno, false); // make sure that corrupt stays usleep (TEST_USLEEP); mark_point(); stop_threads(); mark_point(); wsrep_uuid_t u; wsrep_seqno_t s; bool stb; st.get(u, s, stb); // make sure that mark_corrupt() stays ck_assert(u == WSREP_UUID_UNDEFINED); ck_assert(s == WSREP_SEQNO_UNDEFINED); ck_assert(stb == false); long m, l, w; st.stats(m, l, w); marks += m; locks += l; writes += w; } log_info << "Total marks: " << marks << ", total locks: " << locks << ", total writes: " << writes << "\nlocks ratio: " << (double(locks)/marks) << "\nwrites ratio: " << (double(writes)/locks); unlink (fname); } END_TEST Suite* saved_state_suite() { Suite* s = suite_create ("saved_state"); TCase* tc; tc = tcase_create ("saved_state"); tcase_add_test (tc, test_basic); tcase_add_test (tc, test_unsafe); tcase_add_test (tc, test_corrupt); tcase_set_timeout(tc, 120); suite_add_tcase (s, tc); return s; } galera-4-26.4.22/galera/tests/ist_check.cpp000644 000162 177776 00000045037 14755062442 021560 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2011-2021 Codership Oy // #include "ist.hpp" #include "ist_proto.hpp" #include "trx_handle.hpp" #include "monitor.hpp" #include "replicator_smm.hpp" #include "common.h" #include #include #include using namespace galera; static void register_params(gu::Config& conf) { galera::ist::register_params(conf); galera::ReplicatorSMM::register_params(conf); conf.add(COMMON_BASE_HOST_KEY); conf.add(COMMON_BASE_PORT_KEY); #ifdef GALERA_HAVE_SSL gu::ssl_register_params(conf); #endif // GALERA_HAVE_SSL } static void test_ist_recv_addr_expect(const std::string& expect, const std::string& addr) { ck_assert_msg(expect == addr, "Expected %s got %s", expect.c_str(), addr.c_str()); } START_TEST(test_ist_recv_addr_not_set) { gu::Config conf; register_params(conf); try { galera::IST_determine_recv_addr(conf); ck_abort_msg("Exception not thrown"); } catch (const gu::Exception& e) { ck_assert(e.get_errno() == EINVAL); } } END_TEST START_TEST(test_ist_recv_addr_base_host) { gu::Config conf; register_params(conf); conf.set(COMMON_BASE_HOST_KEY, "127.0.0.1"); test_ist_recv_addr_expect("tcp://127.0.0.1:4568", galera::IST_determine_recv_addr(conf)); } END_TEST START_TEST(test_ist_recv_addr_ip) { gu::Config conf; register_params(conf); conf.set(galera::ist::Receiver::RECV_ADDR, "127.0.0.1"); test_ist_recv_addr_expect("tcp://127.0.0.1:4568", galera::IST_determine_recv_addr(conf)); } END_TEST START_TEST(test_ist_recv_addr_ip_port) { gu::Config conf; register_params(conf); conf.set(galera::ist::Receiver::RECV_ADDR, "127.0.0.1:10001"); test_ist_recv_addr_expect("tcp://127.0.0.1:10001", galera::IST_determine_recv_addr(conf)); } END_TEST START_TEST(test_ist_recv_addr_tcp_ip) { gu::Config conf; register_params(conf); conf.set(galera::ist::Receiver::RECV_ADDR, "tcp://127.0.0.1"); test_ist_recv_addr_expect("tcp://127.0.0.1:4568", galera::IST_determine_recv_addr(conf)); } END_TEST START_TEST(test_ist_recv_addr_tcp_ip_port) { gu::Config conf; register_params(conf); conf.set(galera::ist::Receiver::RECV_ADDR, "tcp://127.0.0.1"); test_ist_recv_addr_expect("tcp://127.0.0.1:4568", galera::IST_determine_recv_addr(conf)); } END_TEST START_TEST(test_ist_recv_bind_not_set) { gu::Config conf; register_params(conf); conf.set(galera::ist::Receiver::RECV_ADDR, "127.0.0.1"); try { (void)galera::IST_determine_recv_bind(conf); ck_abort_msg("Exception not thrown"); } catch (const gu::NotSet&) { } } END_TEST #ifdef GALERA_HAVE_SSL START_TEST(test_ist_recv_addr_auto_ssl_scheme) { gu::Config conf; register_params(conf); // Existing ssl_key parameter should result in ssl scheme if // scheme is not explicitly given. conf.set(gu::conf::ssl_key, "key"); conf.set(galera::ist::Receiver::RECV_ADDR, "127.0.0.1"); test_ist_recv_addr_expect("ssl://127.0.0.1:4568", galera::IST_determine_recv_addr(conf)); } END_TEST START_TEST(test_ist_recv_addr_ssl_scheme) { gu::Config conf; register_params(conf); // Existing ssl_key parameter should result in ssl scheme if // scheme is not explicitly given. conf.set(gu::conf::ssl_key, "key"); conf.set(galera::ist::Receiver::RECV_ADDR, "ssl://127.0.0.1"); test_ist_recv_addr_expect("ssl://127.0.0.1:4568", galera::IST_determine_recv_addr(conf)); } END_TEST #endif // GALERA_HAVE_SSL // Message tests START_TEST(test_ist_message) { using namespace galera::ist; #if 0 /* This is a check for the old (broken) format */ Message m3(3, Message::T_HANDSHAKE, 0x2, 3, 1001); #if GU_WORDSIZE == 32 ck_assert_msg(serial_size(m3) == 20, "serial size %zu != 20", serial_size(m3)); #elif GU_WORDSIZE == 64 ck_assert_msg(serial_size(m3) == 24, "serial size %zu != 24", serial_size(m3)); #endif gu::Buffer buf(m3.serial_size()); m3.serialize(&buf[0], buf.size(), 0); Message mu3(3); mu3.unserialize(&buf[0], buf.size(), 0); ck_assert(mu3.version() == 3); ck_assert(mu3.type() == Message::T_HANDSHAKE); ck_assert(mu3.flags() == 0x2); ck_assert(mu3.ctrl() == 3); ck_assert(mu3.len() == 1001); #endif /* 0 */ Message const m2(VER21, Message::T_HANDSHAKE, 0x2, 3, 1001); size_t const s2(12); ck_assert_msg(m2.serial_size() == s2, "Expected m2.serial_size() = %zd, got %zd", s2, m2.serial_size()); gu::Buffer buf2(m2.serial_size()); m2.serialize(&buf2[0], buf2.size(), 0); Message mu2(VER21); mu2.unserialize(&buf2[0], buf2.size(), 0); ck_assert(mu2.version() == VER21); ck_assert(mu2.type() == Message::T_HANDSHAKE); ck_assert(mu2.flags() == 0x2); ck_assert(mu2.ctrl() == 3); ck_assert(mu2.len() == 1001); Message const m4(VER40, Message::T_HANDSHAKE, 0x2, 3, 1001); size_t const s4(16 + sizeof(uint64_t /* Message::checksum_t */)); ck_assert_msg(m4.serial_size() == s4, "Expected m3.serial_size() = %zd, got %zd", s4, m4.serial_size()); gu::Buffer buf4(m4.serial_size()); m4.serialize(&buf4[0], buf4.size(), 0); Message mu4(VER40); mu4.unserialize(&buf4[0], buf4.size(), 0); ck_assert(mu4.version() == VER40); ck_assert(mu4.type() == Message::T_HANDSHAKE); ck_assert(mu4.flags() == 0x2); ck_assert(mu4.ctrl() == 3); ck_assert(mu4.len() == 1001); } END_TEST // IST tests static gu_barrier_t start_barrier; class TestOrder { public: TestOrder(galera::TrxHandleSlave& trx) : trx_(trx) { } void lock() { } void unlock() { } wsrep_seqno_t seqno() const { return trx_.global_seqno(); } bool condition(wsrep_seqno_t last_entered, wsrep_seqno_t last_left) const { return (last_left >= trx_.depends_seqno()); } #ifdef GU_DBUG_ON void debug_sync(gu::Mutex&) { } #endif // GU_DBUG_ON private: galera::TrxHandleSlave& trx_; }; struct sender_args { gcache::GCache& gcache_; const std::string& peer_; wsrep_seqno_t first_; wsrep_seqno_t last_; int version_; sender_args(gcache::GCache& gcache, const std::string& peer, wsrep_seqno_t first, wsrep_seqno_t last, int version) : gcache_(gcache), peer_ (peer), first_ (first), last_ (last), version_(version) { } }; struct receiver_args { std::string listen_addr_; wsrep_seqno_t first_; wsrep_seqno_t last_; TrxHandleSlave::Pool& trx_pool_; gcache::GCache& gcache_; int version_; receiver_args(const std::string listen_addr, wsrep_seqno_t first, wsrep_seqno_t last, TrxHandleSlave::Pool& sp, gcache::GCache& gc, int version) : listen_addr_(listen_addr), first_ (first), last_ (last), trx_pool_ (sp), gcache_ (gc), version_ (version) { } }; extern "C" void* sender_thd(void* arg) { mark_point(); const sender_args* sargs(reinterpret_cast(arg)); gu::Config conf; galera::ReplicatorSMM::InitConfig(conf, NULL, NULL); gu_barrier_wait(&start_barrier); sargs->gcache_.seqno_lock(sargs->first_); // unlocked in sender dtor galera::ist::Sender sender(conf, sargs->gcache_, sargs->peer_, sargs->version_); mark_point(); sender.send(sargs->first_, sargs->last_, sargs->first_); mark_point(); return 0; } namespace { class ISTHandler : public galera::ist::EventHandler { public: ISTHandler() : mutex_(), cond_(), seqno_(0), eof_(false), error_(0) { } ~ISTHandler() {} void ist_trx(const TrxHandleSlavePtr& ts, bool must_apply, bool preload) override { assert(ts != 0); ts->verify_checksum(); if (ts->state() == TrxHandle::S_ABORTING) { log_info << "ist_trx: aborting: " << ts->global_seqno(); } else { log_info << "ist_trx: " << *ts; ts->set_state(TrxHandle::S_CERTIFYING); } if (preload == false) { assert(seqno_ + 1 == ts->global_seqno()); } else { assert(seqno_ < ts->global_seqno()); } seqno_ = ts->global_seqno(); } void ist_cc(const gcs_action& act, bool must_apply, bool preload) override { gcs_act_cchange const cc(act.buf, act.size); assert(act.seqno_g == cc.seqno); log_info << "ist_cc" << cc.seqno; if (preload == false) { assert(seqno_ + 1 == cc.seqno); } else { assert(seqno_ < cc.seqno); } } void ist_end(const ist::Result& result) override { log_info << "IST ended with status: " << result.error_str; gu::Lock lock(mutex_); error_ = result.error; eof_ = true; cond_.signal(); } int wait() { gu::Lock lock(mutex_); while (eof_ == false) { lock.wait(cond_); } return error_; } private: gu::Mutex mutex_; gu::Cond cond_; wsrep_seqno_t seqno_; bool eof_; int error_; }; } extern "C" void* receiver_thd(void* arg) { mark_point(); receiver_args* rargs(reinterpret_cast(arg)); gu::Config conf; TrxHandleSlave::Pool slave_pool(sizeof(TrxHandleSlave), 1024, "TrxHandleSlave"); galera::ReplicatorSMM::InitConfig(conf, NULL, NULL); mark_point(); conf.set(galera::ist::Receiver::RECV_ADDR, rargs->listen_addr_); ISTHandler isth; galera::ist::Receiver receiver(conf, rargs->gcache_, slave_pool, isth, 0, NULL); // Prepare starts IST receiver thread rargs->listen_addr_ = receiver.prepare(rargs->first_, rargs->last_, rargs->version_, WSREP_UUID_UNDEFINED); gu_barrier_wait(&start_barrier); mark_point(); receiver.ready(rargs->first_); log_info << "IST wait finished with status: " << isth.wait(); receiver.finished(); return 0; } static int select_trx_version(int protocol_version) { // see protocol version table in replicator_smm.hpp switch (protocol_version) { case 1: case 2: return 1; case 3: case 4: return 2; case 5: case 6: case 7: case 8: return 3; case 9: return 4; case 10: return 5; default: ck_abort_msg("unsupported replicator protocol version: %d", protocol_version); } return -1; } static void store_trx(gcache::GCache* const gcache, TrxHandleMaster::Pool& lp, const TrxHandleMaster::Params& trx_params, const wsrep_uuid_t& uuid, int const i) { TrxHandleMasterPtr trx(TrxHandleMaster::New(lp, trx_params, uuid, 1234+i, 5678+i), TrxHandleMasterDeleter()); const wsrep_buf_t key[3] = { {"key1", 4}, {"key2", 4}, {"key3", 4} }; trx->append_key(KeyData(trx_params.version_, key, 3, WSREP_KEY_EXCLUSIVE, true)); trx->append_data("bar", 3, WSREP_DATA_ORDERED, true); assert (i > 0); int last_seen(i - 1); int pa_range(i); gu::byte_t* ptr(0); if (trx_params.version_ < 3) { ck_abort_msg("WS version %d not supported any more", trx_params.version_); } else { galera::WriteSetNG::GatherVector bufs; ssize_t trx_size(trx->gather(bufs)); mark_point(); trx->finalize(last_seen); ptr = static_cast(gcache->malloc(trx_size)); /* concatenate buffer vector */ gu::byte_t* p(ptr); for (size_t k(0); k < bufs->size(); ++k) { ::memcpy(p, bufs[k].ptr, bufs[k].size); p += bufs[k].size; } assert ((p - ptr) == trx_size); gu::Buf ws_buf = { ptr, trx_size }; mark_point(); galera::WriteSetIn wsi(ws_buf); assert (wsi.last_seen() == last_seen); assert (wsi.pa_range() == (wsi.version() < WriteSetNG::VER5 ? 0 : WriteSetNG::MAX_PA_RANGE)); wsi.set_seqno(i, pa_range); assert (wsi.seqno() == int64_t(i)); assert (wsi.pa_range() == pa_range); } gcache->seqno_assign(ptr, i, GCS_ACT_WRITESET, (i - pa_range) <= 0); } static void store_cc(gcache::GCache* const gcache, const wsrep_uuid_t& uuid, int const i) { static int conf_id(0); gcs_act_cchange cc; ::memcpy(&cc.uuid, &uuid, sizeof(uuid)); cc.seqno = i; cc.conf_id = conf_id++; void* tmp; int const cc_size(cc.write(&tmp)); void* const cc_ptr(gcache->malloc(cc_size)); ck_assert(NULL != cc_ptr); memcpy(cc_ptr, tmp, cc_size); free(tmp); gcache->seqno_assign(cc_ptr, i, GCS_ACT_CCHANGE, i > 0); } static void test_ist_common(int const version) { using galera::KeyData; using galera::TrxHandle; using galera::KeyOS; TrxHandleMaster::Pool lp(TrxHandleMaster::LOCAL_STORAGE_SIZE(), 4, "ist_common"); TrxHandleSlave::Pool sp(sizeof(TrxHandleSlave), 4, "ist_common"); int const trx_version(select_trx_version(version)); TrxHandleMaster::Params const trx_params("", trx_version, galera::KeySet::MAX_VERSION); std::string const dir("."); gu::Config conf_sender; galera::ReplicatorSMM::InitConfig(conf_sender, NULL, NULL); std::string const gcache_sender_file("ist_sender.cache"); conf_sender.set("gcache.name", gcache_sender_file); conf_sender.set("gcache.size", "1M"); gcache::GCache* gcache_sender = new gcache::GCache(NULL, conf_sender, dir); gu::Config conf_receiver; galera::ReplicatorSMM::InitConfig(conf_receiver, NULL, NULL); std::string const gcache_receiver_file("ist_receiver.cache"); conf_receiver.set("gcache.name", gcache_receiver_file); conf_receiver.set("gcache.size", "1M"); gcache::GCache* gcache_receiver = new gcache::GCache(NULL, conf_receiver, dir); std::string receiver_addr("tcp://127.0.0.1:0"); wsrep_uuid_t uuid; gu_uuid_generate(reinterpret_cast(&uuid), 0, 0); mark_point(); // populate gcache for (size_t i(1); i <= 10; ++i) { if (i % 3) { store_trx(gcache_sender, lp, trx_params, uuid, i); } else { store_cc(gcache_sender, uuid, i); } } mark_point(); receiver_args rargs(receiver_addr, 1, 10, sp, *gcache_receiver, version); sender_args sargs(*gcache_sender, rargs.listen_addr_, 1, 10, version); gu_barrier_init(&start_barrier, 0, 2); gu_thread_t sender_thread, receiver_thread; gu_thread_create(&sender_thread, 0, &sender_thd, &sargs); mark_point(); usleep(100000); gu_thread_create(&receiver_thread, 0, &receiver_thd, &rargs); mark_point(); gu_thread_join(sender_thread, 0); gu_thread_join(receiver_thread, 0); mark_point(); delete gcache_sender; delete gcache_receiver; mark_point(); unlink(gcache_sender_file.c_str()); unlink(gcache_receiver_file.c_str()); } START_TEST(test_ist_v7) { test_ist_common(7); /* trx ver: 3, STR ver: 2, alignment: none */ } END_TEST START_TEST(test_ist_v8) { test_ist_common(8); /* trx ver: 3, STR ver: 2, alignment: 8 */ } END_TEST START_TEST(test_ist_v9) { test_ist_common(9); /* trx ver: 4, STR ver: 2, alignment: 8 */ } END_TEST START_TEST(test_ist_v10) { test_ist_common(10); /* trx ver: 5, STR ver: 3, alignment: 8 */ } END_TEST Suite* ist_suite() { Suite* s = suite_create("ist"); TCase* tc; tc = tcase_create("test_ist_recv_addr_not_set"); tcase_add_test(tc, test_ist_recv_addr_not_set); suite_add_tcase(s, tc); tc = tcase_create("test_ist_recv_addr_ip"); tcase_add_test(tc, test_ist_recv_addr_ip); suite_add_tcase(s, tc); tc = tcase_create("test_ist_recv_addr_base_host"); tcase_add_test(tc, test_ist_recv_addr_base_host); suite_add_tcase(s, tc); tc = tcase_create("test_ist_recv_addr_ip_port"); tcase_add_test(tc, test_ist_recv_addr_ip_port); suite_add_tcase(s, tc); tc = tcase_create("test_ist_recv_addr_tcp_ip"); tcase_add_test(tc, test_ist_recv_addr_tcp_ip); suite_add_tcase(s, tc); tc = tcase_create("test_ist_recv_addr_tcp_ip_port"); tcase_add_test(tc, test_ist_recv_addr_tcp_ip_port); suite_add_tcase(s, tc); tc = tcase_create("test_ist_recv_bind_not_set"); tcase_add_test(tc, test_ist_recv_bind_not_set); suite_add_tcase(s, tc); #ifdef GALERA_HAVE_SSL tc = tcase_create("test_ist_recv_addr_auto_ssl_scheme"); tcase_add_test(tc, test_ist_recv_addr_auto_ssl_scheme); suite_add_tcase(s, tc); tc = tcase_create("test_ist_recv_addr_ssl_scheme"); tcase_add_test(tc, test_ist_recv_addr_ssl_scheme); suite_add_tcase(s, tc); #endif // GALERA_HAVE_SSL tc = tcase_create("test_ist_message"); tcase_add_test(tc, test_ist_message); suite_add_tcase(s, tc); tc = tcase_create("test_ist_v7"); tcase_set_timeout(tc, 60); suite_add_tcase(s, tc); tcase_add_test(tc, test_ist_v7); tc = tcase_create("test_ist_v8"); tcase_set_timeout(tc, 60); tcase_add_test(tc, test_ist_v8); suite_add_tcase(s, tc); tc = tcase_create("test_ist_v9"); tcase_set_timeout(tc, 60); tcase_add_test(tc, test_ist_v9); suite_add_tcase(s, tc); tc = tcase_create("test_ist_v10"); tcase_set_timeout(tc, 60); tcase_add_test(tc, test_ist_v10); suite_add_tcase(s, tc); return s; } galera-4-26.4.22/galera/tests/service_thd_check.cpp000644 000162 177776 00000007540 14755062442 023255 0ustar00jenkinsnogroup000000 000000 /* * Copyright (C) 2010-2021 Codership Oy */ #include "../src/galera_service_thd.hpp" #include "../src/replicator_smm.hpp" #include "gu_inttypes.hpp" #include #include namespace { class TestEnv { class GCache_setup { public: GCache_setup(gu::Config& conf) : name_("service_thd_check.gcache") { conf.set("gcache.name", name_); conf.set("gcache.size", "1M"); log_info << "conf for gcache: " << conf; } ~GCache_setup() { unlink(name_.c_str()); } private: std::string const name_; }; public: TestEnv() : conf_ (), init_ (conf_, NULL, NULL), gcache_setup_(conf_), gcache_pcb_ (galera::ProgressCallback(WSREP_MEMBER_UNDEFINED, WSREP_MEMBER_UNDEFINED)), gcache_ (&gcache_pcb_, conf_, "."), gcs_ (conf_, gcache_) {} gcache::GCache& gcache() { return gcache_; } galera::DummyGcs& gcs() { return gcs_; } private: gu::Config conf_; galera::ReplicatorSMM::InitConfig init_; GCache_setup gcache_setup_; galera::ProgressCallback gcache_pcb_; gcache::GCache gcache_; galera::DummyGcs gcs_; }; } using namespace galera; START_TEST(service_thd1) { TestEnv env; ServiceThd* thd = new ServiceThd(env.gcs(), env.gcache()); ck_assert(thd != 0); delete thd; } END_TEST #define TEST_USLEEP 1000 // 1ms #define WAIT_FOR(cond) \ { int count = 1000; while (--count && !(cond)) { usleep (TEST_USLEEP); }} START_TEST(service_thd2) { TestEnv env; DummyGcs& conn(env.gcs()); ServiceThd* thd = new ServiceThd(conn, env.gcache()); gu::UUID const state_uuid(NULL, 0); ck_assert(thd != 0); conn.set_last_applied(gu::GTID(state_uuid, 0)); gcs_seqno_t seqno = 1; thd->report_last_committed (seqno); thd->flush(state_uuid); WAIT_FOR(conn.last_applied() == seqno); ck_assert_msg(conn.last_applied() == seqno, "seqno = %" PRId64 ", expected %" PRId64, conn.last_applied(), seqno); seqno = 5; thd->report_last_committed (seqno); thd->flush(state_uuid); WAIT_FOR(conn.last_applied() == seqno); ck_assert_msg(conn.last_applied() == seqno, "seqno = %" PRId64 ", expected %" PRId64, conn.last_applied(), seqno); thd->report_last_committed (3); thd->flush(state_uuid); WAIT_FOR(conn.last_applied() == seqno); ck_assert_msg(conn.last_applied() == seqno, "seqno = %" PRId64 ", expected %" PRId64, conn.last_applied(), seqno); thd->reset(); seqno = 3; thd->report_last_committed (seqno); thd->flush(state_uuid); WAIT_FOR(conn.last_applied() == seqno); ck_assert_msg(conn.last_applied() == seqno, "seqno = %" PRId64 ", expected %" PRId64, conn.last_applied(), seqno); delete thd; } END_TEST START_TEST(service_thd3) { TestEnv env; ServiceThd* thd = new ServiceThd(env.gcs(), env.gcache()); ck_assert(thd != 0); // so far for empty GCache the following should be a noop. thd->release_seqno(-1); thd->release_seqno(2345); thd->release_seqno(234645676); delete thd; } END_TEST Suite* service_thd_suite() { Suite* s = suite_create ("service_thd"); TCase* tc; tc = tcase_create ("service_thd"); tcase_add_test (tc, service_thd1); tcase_add_test (tc, service_thd2); tcase_add_test (tc, service_thd3); tcase_set_timeout(tc, 60); suite_add_tcase (s, tc); return s; } galera-4-26.4.22/galera/tests/progress_check.cpp000644 000162 177776 00000004043 14755062442 022615 0ustar00jenkinsnogroup000000 000000 /* * Copyright (C) 2021 Codership Oy */ #include "../src/progress_callback.hpp" #include #include #include #include #include #include // to sleep in C++ style struct wsrep_event_context { std::vector exp; wsrep_event_context() : exp() { exp.push_back("{ \"from\": 3, \"to\": 4, \"total\": 2, \"done\": 2, \"undefined\": -1 }"); exp.push_back("{ \"from\": 3, \"to\": 4, \"total\": 2, \"done\": 1, \"undefined\": -1 }"); exp.push_back("{ \"from\": 3, \"to\": 4, \"total\": 2, \"done\": 0, \"undefined\": -1 }"); } }; static void event_cb(wsrep_event_context_t* ctx, const char* name, const char* value) { ck_assert(strcmp(name, "progress") == 0); // ensure proper event name const std::string& exp(ctx->exp.back()); ck_assert_msg(strcmp(exp.c_str(), value) == 0, "Expected '%s', got '%s'", exp.c_str(), value); ctx->exp.pop_back(); } START_TEST(progress_callback) { wsrep_event_context_t event_context; wsrep_event_service_v1_t evs = { event_cb, &event_context }; gu::EventService::init_v1(&evs); { galera::ProgressCallback pcb(WSREP_MEMBER_JOINED, WSREP_MEMBER_SYNCED); /* Ctor calls event callback for the first time */ gu::Progress prog(&pcb, "Testing", " units", 2, 1); /* This calls event callback for the second time. Need to sleep * a second here due to certain rate limiting in progress object */ std::this_thread::sleep_for(std::chrono::milliseconds(1000)); prog.update(1); prog.finish(); /* Dtor calls event callback for the third time */ } gu::EventService::deinit_v1(); } END_TEST Suite* progress_suite() { Suite* s = suite_create ("progress_suite"); TCase* tc; tc = tcase_create ("progress_case"); tcase_add_test (tc, progress_callback); tcase_set_timeout(tc, 60); suite_add_tcase (s, tc); return s; } galera-4-26.4.22/galera/tests/CMakeLists.txt000644 000162 177776 00000001274 14755062442 021653 0ustar00jenkinsnogroup000000 000000 # # Copyright (C) 2020 Codership Oy # add_executable(galera_check galera_check.cpp data_set_check.cpp certification_check.cpp key_set_check.cpp write_set_ng_check.cpp trx_handle_check.cpp service_thd_check.cpp ist_check.cpp saved_state_check.cpp defaults_check.cpp progress_check.cpp ) target_include_directories(galera_check PRIVATE ${PROJECT_SOURCE_DIR}/galera/src ${PROJECT_SOURCE_DIR}/wsrep/src ) # TODO: Fix. target_compile_options(galera_check PRIVATE -Wno-conversion -Wno-unused-parameter ) target_link_libraries(galera_check galera_smm_static ${GALERA_UNIT_TEST_LIBS}) add_test( NAME galera_check COMMAND galera_check ) galera-4-26.4.22/galera/tests/SConscript000644 000162 177776 00000003060 14755062442 021120 0ustar00jenkinsnogroup000000 000000 Import('check_env') env = check_env.Clone() # Include paths env.Append(CPPPATH = Split(''' # #/common #/galerautils/src #/gcache/src #/gcs/src #/galera/src ''')) env.Prepend(LIBS=File('#/galerautils/src/libgalerautils.a')) env.Prepend(LIBS=File('#/galerautils/src/libgalerautils++.a')) env.Prepend(LIBS=File('#/gcomm/src/libgcomm.a')) env.Prepend(LIBS=File('#/gcs/src/libgcs.a')) env.Prepend(LIBS=File('#/gcache/src/libgcache.a')) env.Prepend(LIBS=File('#/galera/src/libgalera++.a')) galera_check = env.Program(target='galera_check', source=Split(''' galera_check.cpp data_set_check.cpp key_set_check.cpp write_set_ng_check.cpp certification_check.cpp trx_handle_check.cpp service_thd_check.cpp ist_check.cpp saved_state_check.cpp defaults_check.cpp progress_check.cpp ''')) # write_set_check.cpp stamp = "galera_check.passed" env.Test(stamp, galera_check) env.Alias("test", stamp) Clean(galera_check, ['#/galera_check.log', 'ist_check.cache']) galera-4-26.4.22/galera/tests/write_set_check.cpp000644 000162 177776 00000052056 14755062442 022765 0ustar00jenkinsnogroup000000 000000 /* * Copyright (C) 2010-2020 Codership Oy */ #include "write_set.hpp" #include "mapped_buffer.hpp" #include "gu_logger.hpp" #include "certification.hpp" #include "replicator_smm.hpp" #include "wsdb.cpp" #include "gcs_action_source.hpp" #include "galera_service_thd.hpp" #include "gu_inttypes.hpp" #include #include namespace { class TestEnv { class GCache_setup { public: GCache_setup(gu::Config& conf) : name_("write_set_test.gcache") { conf.set("gcache.name", name_); conf.set("gcache.size", "1M"); log_info << "conf for gcache: " << conf; } ~GCache_setup() { unlink(name_.c_str()); } private: std::string const name_; }; public: TestEnv() : conf_ (), init_ (conf_, NULL, NULL), gcache_setup_(conf_), gcache_ (conf_, "."), gcs_ (conf_, gcache_), thd_ (gcs_, gcache_) {} ~TestEnv() {} gu::Config& conf() { return conf_; } galera::ServiceThd& thd() { return thd_; } private: gu::Config conf_; galera::ReplicatorSMM::InitConfig init_; GCache_setup gcache_setup_; gcache::GCache gcache_; galera::DummyGcs gcs_; galera::ServiceThd thd_; }; } using namespace std; using namespace galera; typedef std::vector KeyPartSequence; START_TEST(test_key1) { static char k1[16]; static char k2[256]; static char k3[1 << 21]; static char k4[(1 << 22) - 5]; memset(k1, 0xab, sizeof(k1)); memset(k2, 0xcd, sizeof(k2)); memset(k3, 0x9e, sizeof(k3)); memset(k4, 0x8f, sizeof(k4)); const wsrep_buf_t kiovec[4] = { {k1, sizeof k1 }, {k2, sizeof k2 }, {k3, sizeof k3 }, {k4, sizeof k4 } }; KeyOS key(1, kiovec, 4, 0); size_t expected_size(0); #ifndef GALERA_KEY_VLQ expected_size += 1 + std::min(sizeof k1, size_t(0xff)); expected_size += 1 + std::min(sizeof k2, size_t(0xff)); expected_size += 1 + std::min(sizeof k3, size_t(0xff)); expected_size += 1 + std::min(sizeof k4, size_t(0xff)); expected_size += sizeof(uint16_t); #else expected_size += gu::uleb128_size(sizeof k1) + sizeof k1; expected_size += gu::uleb128_size(sizeof k2) + sizeof k2; expected_size += gu::uleb128_size(sizeof k3) + sizeof k3; expected_size += gu::uleb128_size(sizeof k4) + sizeof k4; expected_size += gu::uleb128_size(expected_size); #endif ck_assert_msg(key.serial_size() == expected_size, "%ld <-> %ld", key.serial_size(), expected_size); KeyPartSequence kp(key.key_parts()); ck_assert(kp.size() == 4); gu::Buffer buf(key.serial_size()); key.serialize(&buf[0], buf.size(), 0); KeyOS key2(1); key2.unserialize(&buf[0], buf.size(), 0); ck_assert(key2 == key); } END_TEST START_TEST(test_key2) { static char k1[16]; static char k2[256]; static char k3[1 << 21]; static char k4[(1 << 22) - 5]; memset(k1, 0xab, sizeof(k1)); memset(k2, 0xcd, sizeof(k2)); memset(k3, 0x9e, sizeof(k3)); memset(k4, 0x8f, sizeof(k4)); const wsrep_buf_t kiovec[4] = { {k1, sizeof k1 }, {k2, sizeof k2 }, {k3, sizeof k3 }, {k4, sizeof k4 } }; KeyOS key(2, kiovec, 4, 0); size_t expected_size(0); expected_size += 1; // flags #ifndef GALERA_KEY_VLQ expected_size += 1 + std::min(sizeof k1, size_t(0xff)); expected_size += 1 + std::min(sizeof k2, size_t(0xff)); expected_size += 1 + std::min(sizeof k3, size_t(0xff)); expected_size += 1 + std::min(sizeof k4, size_t(0xff)); expected_size += sizeof(uint16_t); #else expected_size += gu::uleb128_size(sizeof k1) + sizeof k1; expected_size += gu::uleb128_size(sizeof k2) + sizeof k2; expected_size += gu::uleb128_size(sizeof k3) + sizeof k3; expected_size += gu::uleb128_size(sizeof k4) + sizeof k4; expected_size += gu::uleb128_size(expected_size); #endif ck_assert_msg(key.serial_size() == expected_size, "%ld <-> %ld", key.serial_size(), expected_size); KeyPartSequence kp(key.key_parts()); ck_assert(kp.size() == 4); gu::Buffer buf(key.serial_size()); key.serialize(&buf[0], buf.size(), 0); KeyOS key2(2); key2.unserialize(&buf[0], buf.size(), 0); ck_assert(key2 == key); } END_TEST START_TEST(test_write_set1) { WriteSet ws(1); const wsrep_buf_t key1[2] = { {void_cast("dbt\0t1"), 6}, {void_cast("aaa") , 3} }; const wsrep_buf_t key2[2] = { {void_cast("dbt\0t2"), 6}, {void_cast("bbbb"), 4} }; const char* rbr = "rbrbuf"; size_t rbr_len = 6; log_info << "ws0 " << ws.serial_size(); ws.append_key(KeyData(1, key1, 2, WSREP_KEY_EXCLUSIVE, true)); log_info << "ws1 " << ws.serial_size(); ws.append_key(KeyData(1, key2, 2, WSREP_KEY_EXCLUSIVE, true)); log_info << "ws2 " << ws.serial_size(); ws.append_data(rbr, rbr_len); gu::Buffer rbrbuf(rbr, rbr + rbr_len); log_info << "rbrlen " << gu::serial_size4(rbrbuf); log_info << "wsrbr " << ws.serial_size(); gu::Buffer buf(ws.serial_size()); ws.serialize(&buf[0], buf.size(), 0); size_t expected_size = 4 // row key sequence size #ifndef GALERA_KEY_VLQ + 2 + 1 + 6 + 1 + 3 // key1 + 2 + 1 + 6 + 1 + 4 // key2 #else + 1 + 1 + 6 + 1 + 3 // key1 + 1 + 1 + 6 + 1 + 4 // key2 #endif + 4 + 6; // rbr ck_assert_msg(buf.size() == expected_size, "%zd <-> %zd <-> %zd", buf.size(), expected_size, ws.serial_size()); WriteSet ws2(0); size_t ret = ws2.unserialize(&buf[0], buf.size(), 0); ck_assert(ret == expected_size); WriteSet::KeySequence rks; ws.get_keys(rks); WriteSet::KeySequence rks2; ws.get_keys(rks2); ck_assert(rks2 == rks); ck_assert(ws2.get_data() == ws.get_data()); } END_TEST START_TEST(test_write_set2) { WriteSet ws(2); const wsrep_buf_t key1[2] = { {void_cast("dbt\0t1"), 6}, {void_cast("aaa") , 3} }; const wsrep_buf_t key2[2] = { {void_cast("dbt\0t2"), 6}, {void_cast("bbbb"), 4} }; const char* rbr = "rbrbuf"; size_t rbr_len = 6; log_info << "ws0 " << ws.serial_size(); ws.append_key(KeyData(2, key1, 2, WSREP_KEY_EXCLUSIVE, true)); log_info << "ws1 " << ws.serial_size(); ws.append_key(KeyData(2, key2, 2, WSREP_KEY_EXCLUSIVE, true)); log_info << "ws2 " << ws.serial_size(); ws.append_data(rbr, rbr_len); gu::Buffer rbrbuf(rbr, rbr + rbr_len); log_info << "rbrlen " << gu::serial_size4(rbrbuf); log_info << "wsrbr " << ws.serial_size(); gu::Buffer buf(ws.serial_size()); ws.serialize(&buf[0], buf.size(), 0); size_t expected_size = 4 // row key sequence size #ifndef GALERA_KEY_VLQ + 2 + 1 + 1 + 6 + 1 + 3 // key1 + 2 + 1 + 1 + 6 + 1 + 4 // key2 #else + 1 + 1 + 6 + 1 + 3 // key1 + 1 + 1 + 6 + 1 + 4 // key2 #endif + 4 + 6; // rbr ck_assert_msg(buf.size() == expected_size, "%zd <-> %zd <-> %zd", buf.size(), expected_size, ws.serial_size()); WriteSet ws2(2); size_t ret = ws2.unserialize(&buf[0], buf.size(), 0); ck_assert(ret == expected_size); WriteSet::KeySequence rks; ws.get_keys(rks); WriteSet::KeySequence rks2; ws2.get_keys(rks2); ck_assert(rks2 == rks); ck_assert(ws2.get_data() == ws.get_data()); } END_TEST START_TEST(test_mapped_buffer) { string wd("/tmp"); MappedBuffer mb(wd, 1 << 8); mb.resize(16); for (size_t i = 0; i < 16; ++i) { mb[i] = static_cast(i); } mb.resize(1 << 8); for (size_t i = 0; i < 16; ++i) { ck_assert(mb[i] == static_cast(i)); } for (size_t i = 16; i < (1 << 8); ++i) { mb[i] = static_cast(i); } mb.resize(1 << 20); for (size_t i = 0; i < (1 << 8); ++i) { ck_assert(mb[i] == static_cast(i)); } for (size_t i = 0; i < (1 << 20); ++i) { mb[i] = static_cast(i); } } END_TEST static TrxHandle::LocalPool lp(TrxHandle::LOCAL_STORAGE_SIZE(), 4, "ws_local_pool"); static TrxHandle::SlavePool sp(sizeof(TrxHandle), 4, "ws_slave_pool"); START_TEST(test_cert_hierarchical_v1) { log_info << "test_cert_hierarchical_v1"; struct wsinfo_ { wsrep_uuid_t uuid; wsrep_conn_id_t conn_id; wsrep_trx_id_t trx_id; wsrep_buf_t key[3]; size_t iov_len; wsrep_seqno_t local_seqno; wsrep_seqno_t global_seqno; wsrep_seqno_t last_seen_seqno; wsrep_seqno_t expected_depends_seqno; int flags; Certification::TestResult result; } wsi[] = { // 1 - 3, test symmetric case for dependencies // 1: no dependencies { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, 1, 1, 0, 0, 0, Certification::TEST_OK}, // 2: depends on 1, no conflict { { {1, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, 2, 2, 0, 1, 0, Certification::TEST_OK}, // 3: depends on 2, no conflict { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, 3, 3, 0, 2, 0, Certification::TEST_OK}, // 4 - 8, test symmetric case for conflicts // 4: depends on 3, no conflict { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, 4, 4, 3, 3, 0, Certification::TEST_OK}, // 5: conflict with 4 { { {2, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, 5, 5, 3, -1, 0, Certification::TEST_FAILED}, // 6: depends on 4 (failed 5 not present in index), no conflict { { {2, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, 6, 6, 5, 4, 0, Certification::TEST_OK}, // 7: conflicts with 6 { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, 7, 7, 5, -1, 0, Certification::TEST_FAILED}, // 8: to isolation: must not conflict, depends on global_seqno - 1 { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, 8, 8, 5, 7, TrxHandle::F_ISOLATION, Certification::TEST_OK}, // 9: to isolation: must not conflict, depends on global_seqno - 1 { { {2, } }, 1, 1, { {void_cast("1"), 1}, }, 1, 9, 9, 5, 8, TrxHandle::F_ISOLATION, Certification::TEST_OK}, }; size_t nws(sizeof(wsi)/sizeof(wsi[0])); TestEnv env; galera::Certification cert(env.conf(), env.thd()); int const version(1); cert.assign_initial_position(0, version); galera::TrxHandle::Params const trx_params("", version,KeySet::MAX_VERSION); mark_point(); for (size_t i(0); i < nws; ++i) { TrxHandle* trx(TrxHandle::New(lp, trx_params, wsi[i].uuid, wsi[i].conn_id, wsi[i].trx_id)); trx->append_key(KeyData(1, wsi[i].key, wsi[i].iov_len, WSREP_KEY_EXCLUSIVE, true)); trx->set_last_seen_seqno(wsi[i].last_seen_seqno); trx->set_flags(trx->flags() | wsi[i].flags); trx->flush(0); // serialize/unserialize to verify that ver1 trx is serializable const galera::MappedBuffer& wc(trx->write_set_collection()); gu::Buffer buf(wc.size()); std::copy(&wc[0], &wc[0] + wc.size(), &buf[0]); trx->unref(); trx = TrxHandle::New(sp); size_t offset(trx->unserialize(&buf[0], buf.size(), 0)); log_info << "ws[" << i << "]: " << buf.size() - offset; trx->append_write_set(&buf[0] + offset, buf.size() - offset); trx->set_received(0, wsi[i].local_seqno, wsi[i].global_seqno); Certification::TestResult result(cert.append_trx(trx)); ck_assert_msg(result == wsi[i].result, "wsi: %zu, g: %" PRId64 " r: %d er: %d", i, trx->global_seqno(), result, wsi[i].result); ck_assert_msg(trx->depends_seqno() == wsi[i].expected_depends_seqno, "wsi: %zu g: %" PRId64 " ld: %" PRId64 " eld: %" PRId64, i, trx->global_seqno(), trx->depends_seqno(), wsi[i].expected_depends_seqno); cert.set_trx_committed(trx); trx->unref(); } } END_TEST START_TEST(test_cert_hierarchical_v2) { log_info << "test_cert_hierarchical_v2"; const int version(2); struct wsinfo_ { wsrep_uuid_t uuid; wsrep_conn_id_t conn_id; wsrep_trx_id_t trx_id; wsrep_buf_t key[3]; size_t iov_len; bool shared; wsrep_seqno_t local_seqno; wsrep_seqno_t global_seqno; wsrep_seqno_t last_seen_seqno; wsrep_seqno_t expected_depends_seqno; int flags; Certification::TestResult result; } wsi[] = { // 1 - 4: shared - shared // First four cases are shared keys, they should not collide or // generate dependency // 1: no dependencies { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, true, 1, 1, 0, 0, 0, Certification::TEST_OK}, // 2: no dependencies { { {1, } }, 1, 2, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, true, 2, 2, 0, 0, 0, Certification::TEST_OK}, // 3: no dependencies { { {2, } }, 1, 3, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, true, 3, 3, 0, 0, 0, Certification::TEST_OK}, // 4: no dependencies { { {3, } }, 1, 4, { {void_cast("1"), 1}, }, 1, true, 4, 4, 0, 0, 0, Certification::TEST_OK}, // 5: shared - exclusive // 5: depends on 4 { { {2, } }, 1, 5, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, false, 5, 5, 0, 4, 0, Certification::TEST_OK}, // 6 - 8: exclusive - shared // 6: collides with 5 { { {1, } }, 1, 6, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, true, 6, 6, 4, -1, 0, Certification::TEST_FAILED}, // 7: collides with 5 { { {1, } }, 1, 7, { {void_cast("1"), 1}, }, 1, true, 7, 7, 4, -1, 0, Certification::TEST_FAILED}, // 8: collides with 5 { { {1, } }, 1, 8, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1}}, 3, true, 8, 8, 4, -1, 0, Certification::TEST_FAILED}, // 9 - 10: shared key shadows dependency to 5 // 9: depends on 5 { { {2, } }, 1, 9, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, true, 9, 9, 0, 5, 0, Certification::TEST_OK}, // 10: depends on 5 { { {2, } }, 1, 10, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, true, 10, 10, 6, 5, 0, Certification::TEST_OK}, // 11 - 13: exclusive - shared - exclusive dependency { { {2, } }, 1, 11, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, false, 11, 11, 10, 10, 0, Certification::TEST_OK}, { { {2, } }, 1, 12, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, true, 12, 12, 10, 11, 0, Certification::TEST_OK}, { { {2, } }, 1, 13, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, false, 13, 13, 10, 12, 0, Certification::TEST_OK}, }; size_t nws(sizeof(wsi)/sizeof(wsi[0])); TestEnv env; galera::Certification cert(env.conf(), env.thd()); cert.assign_initial_position(0, version); galera::TrxHandle::Params const trx_params("", version,KeySet::MAX_VERSION); mark_point(); for (size_t i(0); i < nws; ++i) { TrxHandle* trx(TrxHandle::New(lp, trx_params, wsi[i].uuid, wsi[i].conn_id, wsi[i].trx_id)); trx->append_key(KeyData(version, wsi[i].key, wsi[i].iov_len, (wsi[i].shared ? WSREP_KEY_SHARED : WSREP_KEY_EXCLUSIVE), true)); trx->set_last_seen_seqno(wsi[i].last_seen_seqno); trx->set_flags(trx->flags() | wsi[i].flags); trx->flush(0); // serialize/unserialize to verify that ver1 trx is serializable const galera::MappedBuffer& wc(trx->write_set_collection()); gu::Buffer buf(wc.size()); std::copy(&wc[0], &wc[0] + wc.size(), &buf[0]); trx->unref(); trx = TrxHandle::New(sp); size_t offset(trx->unserialize(&buf[0], buf.size(), 0)); log_info << "ws[" << i << "]: " << buf.size() - offset; trx->append_write_set(&buf[0] + offset, buf.size() - offset); trx->set_received(0, wsi[i].local_seqno, wsi[i].global_seqno); Certification::TestResult result(cert.append_trx(trx)); ck_assert_msg(result == wsi[i].result, "g: %" PRId64 " res: %d exp: %d", trx->global_seqno(), result, wsi[i].result); ck_assert_msg(trx->depends_seqno() == wsi[i].expected_depends_seqno, "wsi: %zu g: %" PRId64 " ld: %" PRId64 " eld: %" PRId64, i, trx->global_seqno(), trx->depends_seqno(), wsi[i].expected_depends_seqno); cert.set_trx_committed(trx); trx->unref(); } } END_TEST // This test leaks memory and it is for trx protocol version 2 // which is pre 25.3.5. Disabling this test for now with ASAN // build. The test should be either removed or fixed to work // with more recent protocol versions. #ifndef GALERA_WITH_ASAN START_TEST(test_trac_726) { log_info << "test_trac_726"; const int version(2); TestEnv env; galera::Certification cert(env.conf(), env.thd()); galera::TrxHandle::Params const trx_params("", version,KeySet::MAX_VERSION); wsrep_uuid_t uuid1 = {{1, }}; wsrep_uuid_t uuid2 = {{2, }}; cert.assign_initial_position(0, version); mark_point(); wsrep_buf_t key1 = {void_cast("1"), 1}; wsrep_buf_t key2 = {void_cast("2"), 1}; { TrxHandle* trx(TrxHandle::New(lp, trx_params, uuid1, 0, 0)); trx->append_key(KeyData(version, &key1, 1, WSREP_KEY_EXCLUSIVE, true)); trx->set_last_seen_seqno(0); trx->flush(0); // serialize/unserialize to verify that ver1 trx is serializable const galera::MappedBuffer& wc(trx->write_set_collection()); gu::Buffer buf(wc.size()); std::copy(&wc[0], &wc[0] + wc.size(), &buf[0]); trx->unref(); trx = TrxHandle::New(sp); size_t offset(trx->unserialize(&buf[0], buf.size(), 0)); trx->append_write_set(&buf[0] + offset, buf.size() - offset); trx->set_received(0, 1, 1); Certification::TestResult result(cert.append_trx(trx)); ck_assert(result == Certification::TEST_OK); cert.set_trx_committed(trx); trx->unref(); } { TrxHandle* trx(TrxHandle::New(lp, trx_params, uuid2, 0, 0)); trx->append_key(KeyData(version, &key2, 1, WSREP_KEY_EXCLUSIVE, true)); trx->append_key(KeyData(version, &key2, 1, WSREP_KEY_SHARED, true)); trx->append_key(KeyData(version, &key1, 1, WSREP_KEY_EXCLUSIVE, true)); trx->set_last_seen_seqno(0); trx->flush(0); // serialize/unserialize to verify that ver1 trx is serializable const galera::MappedBuffer& wc(trx->write_set_collection()); gu::Buffer buf(wc.size()); std::copy(&wc[0], &wc[0] + wc.size(), &buf[0]); trx->unref(); trx = TrxHandle::New(sp); size_t offset(trx->unserialize(&buf[0], buf.size(), 0)); trx->append_write_set(&buf[0] + offset, buf.size() - offset); trx->set_received(0, 2, 2); Certification::TestResult result(cert.append_trx(trx)); ck_assert(result == Certification::TEST_FAILED); cert.set_trx_committed(trx); trx->unref(); } } END_TEST #endif // GALERA_WITH_ASAN Suite* write_set_suite() { Suite* s = suite_create("write_set"); TCase* tc; tc = tcase_create("test_key1"); tcase_add_test(tc, test_key1); suite_add_tcase(s, tc); tc = tcase_create("test_key2"); tcase_add_test(tc, test_key2); suite_add_tcase(s, tc); tc = tcase_create("test_write_set1"); tcase_add_test(tc, test_write_set1); suite_add_tcase(s, tc); tc = tcase_create("test_write_set2"); tcase_add_test(tc, test_write_set2); suite_add_tcase(s, tc); tc = tcase_create("test_mapped_buffer"); tcase_add_test(tc, test_mapped_buffer); suite_add_tcase(s, tc); tc = tcase_create("test_cert_hierarchical_v1"); tcase_add_test(tc, test_cert_hierarchical_v1); tcase_set_timeout(tc, 20); suite_add_tcase(s, tc); tc = tcase_create("test_cert_hierarchical_v2"); tcase_add_test(tc, test_cert_hierarchical_v2); tcase_set_timeout(tc, 120); suite_add_tcase(s, tc); #ifndef GALERA_WITH_ASAN tc = tcase_create("test_trac_726"); tcase_add_test(tc, test_trac_726); tcase_set_timeout(tc, 20); suite_add_tcase(s, tc); #endif return s; } galera-4-26.4.22/galera/tests/test_key.hpp000644 000162 177776 00000004506 14755062442 021454 0ustar00jenkinsnogroup000000 000000 /* Copyright (C) 2013-2024 Codership Oy * * $Id$ */ #ifndef _TEST_KEY_HPP_ #define _TEST_KEY_HPP_ #include "../src/key_data.hpp" #include "../src/key_set.hpp" // for version_to_hash_size #include #include using namespace galera; class TestKey { public: TestKey (int const ver, wsrep_key_type_t const type, std::vector const parts, bool const copy = true) : parts_ (), ver_ (ver), type_ (type), copy_ (copy) { parts_.reserve(parts.size()); for (size_t i = 0; i < parts.size(); ++i) { size_t p_len(parts[i] ? strlen(parts[i]) + 1 : 0); wsrep_buf_t b = { parts[i], p_len }; parts_.push_back(b); } } TestKey (int const ver, wsrep_key_type_t const type, bool const copy, const char* const part0, const char* const part1 = 0, const char* const part2 = 0, const char* const part3 = 0, const char* const part4 = 0, const char* const part5 = 0, const char* const part6 = 0, const char* const part7 = 0, const char* const part8 = 0, const char* const part9 = 0 ) : parts_ (), ver_ (ver), type_ (type), copy_ (copy) { parts_.reserve(10); (push_back(part0) && push_back(part1) && push_back(part2) && push_back(part3) && push_back(part4) && push_back(part5) && push_back(part6) && push_back(part7) && push_back(part8) && push_back(part9)); } KeyData operator() () { return KeyData (ver_, parts_.data(), parts_.size(), type_, copy_); } private: std::vector parts_; int const ver_; wsrep_key_type_t const type_; bool const copy_; bool push_back (const char* const p) { size_t p_len(-1); if (p && (p_len = strlen(p) + 1) > 0) { wsrep_buf_t b = { p, p_len }; parts_.push_back(b); return true; } return false; } }; #endif /* _TEST_KEY_HPP_ */ galera-4-26.4.22/galera/tests/certification_check.cpp000644 000162 177776 00000162144 14755062442 023603 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2015-2024 Codership Oy // #include "replicator_smm.hpp" // ReplicatorSMM::InitConfig #include "certification.hpp" #include "trx_handle.hpp" #include "key_os.hpp" #include "GCache.hpp" #include "gu_config.hpp" #include "gu_inttypes.hpp" #include "test_key.hpp" #include namespace { class TestEnv { public: TestEnv() : conf_ (), init_ (conf_), gcache_pcb_ (galera::ProgressCallback(WSREP_MEMBER_UNDEFINED, WSREP_MEMBER_UNDEFINED)), gcache_ (&gcache_pcb_, conf_, ".") { } ~TestEnv() { ::unlink(GCACHE_NAME.c_str()); } gu::Config& conf() { return conf_ ; } gcache::GCache& gcache() { return gcache_; } private: static std::string const GCACHE_NAME; gu::Config conf_; struct Init { galera::ReplicatorSMM::InitConfig init_; Init(gu::Config& conf) : init_(conf, NULL, NULL) { conf.set("gcache.name", GCACHE_NAME); conf.set("gcache.size", "1M"); } } init_; galera::ProgressCallback gcache_pcb_; gcache::GCache gcache_; }; struct WSInfo { wsrep_uuid_t uuid; wsrep_conn_id_t conn_id; wsrep_trx_id_t trx_id; wsrep_buf_t key[3]; size_t iov_len; bool shared; wsrep_seqno_t local_seqno; wsrep_seqno_t global_seqno; wsrep_seqno_t last_seen_seqno; wsrep_seqno_t expected_depends_seqno; int flags; wsrep_key_type_t zero_level; // type of the zero-level key galera::Certification::TestResult result; const char data_ptr[24]; size_t data_len; }; } std::string const TestEnv::GCACHE_NAME = "cert.cache"; static void run_wsinfo(const WSInfo* const wsi, size_t const nws, int const version) { galera::TrxHandleMaster::Pool mp( sizeof(galera::TrxHandleMaster) + sizeof(galera::WriteSetOut), 16, "certification_mp"); galera::TrxHandleSlave::Pool sp( sizeof(galera::TrxHandleSlave), 16, "certification_sp"); TestEnv env; galera::Certification cert(env.conf(), 0); cert.assign_initial_position(gu::GTID(), version); galera::TrxHandleMaster::Params const trx_params( "", version, galera::KeySet::MAX_VERSION); mark_point(); for (size_t i(0); i < nws; ++i) { log_info << "%%%%%%%% Processing WS: " << i << " ver: " << version << " l: " << wsi[i].local_seqno << " g: " << wsi[i].global_seqno << " s: " << wsi[i].last_seen_seqno << " leaf: " << (wsi[i].shared ? WSREP_KEY_REFERENCE : WSREP_KEY_EXCLUSIVE) << " base: " << wsi[i].zero_level; galera::TrxHandleMasterPtr trx(galera::TrxHandleMaster::New( mp, trx_params, wsi[i].uuid, wsi[i].conn_id, wsi[i].trx_id), galera::TrxHandleMasterDeleter()); trx->set_flags(wsi[i].flags); trx->append_key( galera::KeyData(version, wsi[i].key, wsi[i].iov_len, (wsi[i].shared ? galera::KeyData::BRANCH_KEY_TYPE : WSREP_KEY_EXCLUSIVE), true)); if (version >= 6) // version from which zero-level keys were introduced { if (galera::KeyData::BRANCH_KEY_TYPE != wsi[i].zero_level) { trx->append_key(galera::KeyData(version, wsi[i].zero_level)); } // this is always called last in ReplicatorSMM::replicate() trx->append_key(galera::KeyData(version)); } if (wsi[i].data_len) { trx->append_data(wsi[i].data_ptr, wsi[i].data_len, WSREP_DATA_ORDERED, false); } galera::WriteSetNG::GatherVector out; size_t size(trx->write_set_out().gather(trx->source_id(), trx->conn_id(), trx->trx_id(), out)); trx->finalize(wsi[i].last_seen_seqno); // serialize write set into gcache buffer gu::byte_t* buf(static_cast(env.gcache().malloc(size))); ck_assert(out.serialize(buf, size) == size); gcs_action act = {wsi[i].global_seqno, wsi[i].local_seqno, buf, static_cast(size), GCS_ACT_WRITESET}; galera::TrxHandleSlavePtr ts(galera::TrxHandleSlave::New(false, sp), galera::TrxHandleSlaveDeleter()); ck_assert(ts->unserialize(act) == size); galera::Certification::TestResult result(cert.append_trx(ts)); ck_assert_msg(result == wsi[i].result, "g: %" PRId64 " res: %d exp: %d, version: %d", ts->global_seqno(), result, wsi[i].result, version); ck_assert_msg(ts->depends_seqno() == wsi[i].expected_depends_seqno, "wsi: %zu g: %" PRId64 " ld: %" PRId64 " eld: %" PRId64 ", version: %d", i, ts->global_seqno(), ts->depends_seqno(), wsi[i].expected_depends_seqno, version); cert.set_trx_committed(*ts); if (ts->nbo_end() && ts->ends_nbo() != WSREP_SEQNO_UNDEFINED) { cert.erase_nbo_ctx(ts->ends_nbo()); } } } START_TEST(test_certification_trx_v4) { const int version(4); using galera::Certification; using galera::TrxHandle; using galera::void_cast; // TRX certification rules: // * WSInfo wsi[] = { // 1 - 4: shared - shared // First four cases are shared keys, they should not collide or // generate dependency // 1: no dependencies { { {1, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, true, 1, 1, 0, 0, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // 2: no dependencies { { {1, } }, 1, 2, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, true, 2, 2, 0, 0, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK , {0}, 0}, // 3: no dependencies { { {2, } }, 1, 3, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, true, 3, 3, 0, 0, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // 4: no dependencies { { {3, } }, 1, 4, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, true, 4, 4, 0, 0, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // 5: shared - exclusive // 5: depends on 4 { { {2, } }, 1, 5, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, false, 5, 5, 4, 4, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // 6 - 8: exclusive - shared // 6: collides with 5 { { {1, } }, 1, 6, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, true, 6, 6, 4, 5, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_FAILED, {0}, 0}, // 7: depends on 5 { { {2, } }, 1, 7, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, true, 7, 7, 4, 5, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // 8: collides with 5 { { {1, } }, 1, 8, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1}}, 3, true, 8, 8, 4, 5, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_FAILED, {0}, 0}, // 9 - 10: shared key shadows dependency to 5 // 9: depends on 5 { { {2, } }, 1, 9, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, true, 9, 9, 0, 5, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // 10: depends on 5 { { {2, } }, 1, 10, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, true, 10, 10, 6, 5, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // 11 - 13: exclusive - shared - exclusive dependency { { {2, } }, 1, 11, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, false, 11, 11, 10, 10, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, { { {2, } }, 1, 12, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, true, 12, 12, 10, 11, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, { { {2, } }, 1, 13, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, false, 13, 13, 10, 12, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // 14: conflicts with 13 { { {1, } }, 1, 14, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1}}, 3, false, 14, 14, 12, 13, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_FAILED, {0}, 0} }; size_t nws(sizeof(wsi)/sizeof(wsi[0])); run_wsinfo(wsi, nws, version); } END_TEST START_TEST(test_certification_trx_different_level_v4) { const int version(4); using galera::Certification; using galera::TrxHandle; using galera::void_cast; // // Test the following cases: // 1) exclusive (k1, k2, k3) <-> exclusive (k1, k2) -> conflict // 2) exclusive (k1, k2) <-> exclusive (k1, k2, k3) -> conflict // WSInfo wsi[] = { // 1) { { {1, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, false, 1, 1, 0, 0, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, { { {2, } }, 2, 2, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, false, 2, 2, 0, 1, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_FAILED, {0}, 0}, // 2) { { {2, } }, 2, 2, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, false, 3, 3, 2, 1, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, { { {1, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, false, 4, 4, 2, 3, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_FAILED, {0}, 0} }; size_t nws(sizeof(wsi)/sizeof(wsi[0])); run_wsinfo(wsi, nws, version); } END_TEST START_TEST(test_certification_toi_v3) { const int version(3); using galera::Certification; using galera::TrxHandle; using galera::void_cast; // Note that only exclusive keys are used for TOI. // TRX - TOI and TOI - TOI matches: // * TOI should always depend on preceding write set // TOI - TRX matches: // * if coming from the same source, dependency // * if coming from different sources, conflict // TOI - TOI matches: // * always dependency WSInfo wsi[] = { // TOI { { {1, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1}, }, 2, false, 1, 1, 0, 0, TrxHandle::F_ISOLATION | TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // TOI 2 Depends on TOI 1 { { {2, } }, 2, 2, { {void_cast("1"), 1}, }, 1, false, 2, 2, 0, 1, TrxHandle::F_ISOLATION | TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // Trx 3 from the same source depends on TOI 2 { { {2, } }, 3, 3, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1}}, 3, false, 3, 3, 2, 2, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // Trx 4 from different source conflicts with 3 { { {3, } }, 3, 3, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1}}, 3, false, 4, 4, 2, 3, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_FAILED, {0}, 0}, // Non conflicting TOI 5 depends on 4 { { {1, } }, 2, 2, { {void_cast("2"), 1}, }, 1, false, 5, 5, 0, 4, TrxHandle::F_ISOLATION | TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // Trx 6 from different source conflicts with TOI 5 { { {3, } }, 3, 3, { {void_cast("2"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1}}, 3, false, 6, 6, 4, 5, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_FAILED, {0}, 0} }; size_t nws(sizeof(wsi)/sizeof(wsi[0])); run_wsinfo(wsi, nws, version); } END_TEST START_TEST(test_certification_nbo) { log_info << "START: test_certification_nbo"; const int version(galera::WriteSetNG::VER5); using galera::Certification; using galera::TrxHandle; using galera::void_cast; // Non blocking operations with respect to TOI // NBO - TOI: Always conflict // TOI - NBO: Always dependency WSInfo wsi[] = { // 1 - 2: NBO(1) - TOI(2) // 1 - 3: NBO(1) - NBO(3) { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, false, 1, 1, 0, 0, TrxHandle::F_ISOLATION | TrxHandle::F_BEGIN, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, { { {1, } }, 2, 2, { {void_cast("1"), 1}, }, 1, false, 2, 2, 0, 1, TrxHandle::F_ISOLATION | TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_FAILED, {0}, 0}, { { {1, } }, 3, 3, { {void_cast("1"), 1}, }, 1, false, 3, 3, 0, 2, TrxHandle::F_ISOLATION | TrxHandle::F_BEGIN, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_FAILED, {0}, 0}, // 4 - 5 no conflict, different key { { {1, } }, 4, 4, { {void_cast("2"), 1}, }, 1, false, 4, 4, 0, 3, TrxHandle::F_ISOLATION | TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, { { {2, } }, 5, 5, { {void_cast("2"), 1}, }, 1, false, 5, 5, 0, 4, TrxHandle::F_ISOLATION | TrxHandle::F_BEGIN, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // 6 ends the NBO with key 1 // notice the same uuid, conn_id/trx_id as the first entry { { {1, } }, 1, 1, { {void_cast("1"), 1}, }, 1, false, 6, 6, 0, 5, TrxHandle::F_ISOLATION | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, 24 }, // 7 should now succeed { { {1, } }, 7, 7, { {void_cast("1"), 1}, }, 1, false, 7, 7, 0, 6, TrxHandle::F_ISOLATION | TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // Complete seqno 5 to clean up { { {2, } }, 8, 8, { {void_cast("2"), 1}, }, 1, false, 8, 8, 0, 7, TrxHandle::F_ISOLATION | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0}, 24 } }; size_t nws(sizeof(wsi)/sizeof(wsi[0])); run_wsinfo(wsi, nws, version); log_info << "END: test_certification_nbo"; } END_TEST START_TEST(test_certification_commit_fragment) { const int version(4); using galera::Certification; using galera::TrxHandle; using galera::void_cast; WSInfo wsi[] = { // commit fragment vs commit fragment { { {1, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, true, 1, 1, 0, 0, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT | TrxHandle::F_PA_UNSAFE, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, { { {2, } }, 2, 2, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, true, 2, 2, 0, 1, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT | TrxHandle::F_PA_UNSAFE, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // TOI vs commit fragment { { {2, } }, 2, 2, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, false, 3, 3, 2, 2, TrxHandle::F_ISOLATION | TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, { { {1, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, true, 4, 4, 2, 3, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT | TrxHandle::F_PA_UNSAFE, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_FAILED, {0}, 0}, // commit fragment vs TOI { { {2, } }, 2, 2, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, true, 5, 5, 3, 4, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT | TrxHandle::F_PA_UNSAFE, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, { { {1, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1} }, 2, false, 6, 6, 4, 5, TrxHandle::F_ISOLATION | TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0} }; size_t nws(sizeof(wsi)/sizeof(wsi[0])); run_wsinfo(wsi, nws, version); } END_TEST START_TEST(test_certification_zero_level) { const int version(6); using galera::Certification; using galera::TrxHandle; using galera::void_cast; // Interaction of a zero-level non-REFERENCE key with "regular" transactions // "Regular" transaction has a zero-level key, so regarless of TOI or // non-TOI, shared or exclusive, it shall interact as a REFERENCE key trx: // conflict: // * REFERENCE,EXCLUSIVE - EXCLUSIVE conflicts with REFERENCE // * EXCLUSIVE,REFERENCE - REFERENCE conflicts with EXCLUSIVE WSInfo wsi[] = { // 1: no dependencies { { {1, } }, 1, 1, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, true, 1, 1, 0, 0, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // 2: exclusive zero-level same source depends on 1 { { {1, } }, 1, 2, {}, 0, true, 2, 2, 0, 1, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, WSREP_KEY_EXCLUSIVE, Certification::TEST_OK, {0}, 0}, // 3: default zero-level last seen 1 - conflict with 2 { { {2, } }, 1, 3, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, true, 3, 3, 1, 2, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_FAILED, {0}, 0}, // 4: depends on 2 { { {3, } }, 1, 4, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, true, 4, 4, 2, 2, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // 5: exclusive depends on 4, conflicts with 2 { { {1, } }, 1, 5, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, false, 5, 5, 0, 4, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_FAILED, {0}, 0}, // 6: reference depends but does not conflict with 2 because same source { { {1, } }, 1, 6, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, true, 6, 6, 1, 2, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // 7: exclusive, saw 2, conflicts with 6 { { {2, } }, 1, 7, { {void_cast("1"), 1}, {void_cast("1"), 1}, {void_cast("1"), 1} }, 3, false, 7, 7, 2, 6, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_FAILED, {0}, 0}, // 8: exclusive zero-level depends on 6 because same source { { {1, } }, 1, 8, {}, 0, true, 8, 8, 4, 6, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, WSREP_KEY_EXCLUSIVE, Certification::TEST_OK, {0}, 0}, // 9: exclusive zero-level conflicts with exclusive zero-level 8 { { {2, } }, 1, 9, {}, 0, true, 9, 9, 6, 8, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, WSREP_KEY_EXCLUSIVE, Certification::TEST_FAILED, {0}, 0}, // TOI 1 depends on zero-level 8 { { {2, } }, 1, 1, { {void_cast("1"), 1}, }, 1, false, 10, 10, 7, 9, TrxHandle::F_ISOLATION | TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // TOI 2 Depends on zero-level 8 (same source) { { {1, } }, 2, 2, { {void_cast("1"), 1}, }, 1, false, 11, 11, 3, 10, TrxHandle::F_ISOLATION | TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, galera::KeyData::BRANCH_KEY_TYPE, Certification::TEST_OK, {0}, 0}, // zero-level 12 from the different source conflicts with TOI 2 { { {2, } }, 3, 3, {}, 0, true, 12, 12, 10, 11, TrxHandle::F_BEGIN | TrxHandle::F_COMMIT, WSREP_KEY_EXCLUSIVE, Certification::TEST_FAILED, {0}, 0}, }; size_t nws(sizeof(wsi)/sizeof(wsi[0])); run_wsinfo(wsi, nws, version); } END_TEST using CertResult = galera::Certification::TestResult; struct CertFixture { gu::Config conf{}; struct InitConf { galera::ReplicatorSMM::InitConfig init; InitConf(gu::Config& conf) : init(conf, NULL, NULL) { conf.set("gcache.name", "cert_fixture.cache"); conf.set("gcache.size", "1M"); } } init_conf{conf}; galera::TrxHandleMaster::Pool mp{ sizeof(galera::TrxHandleMaster) + sizeof(galera::WriteSetOut), 16, "certification_mp" }; galera::TrxHandleSlave::Pool sp{ sizeof(galera::TrxHandleSlave), 16, "certification_sp" }; galera::ProgressCallback gcache_pcb{WSREP_MEMBER_UNDEFINED, WSREP_MEMBER_UNDEFINED}; gcache::GCache gcache{&gcache_pcb, conf, "."}; galera::Certification cert{conf, 0}; int version = galera::WriteSetNG::MAX_VERSION; CertFixture() { cert.assign_initial_position(gu::GTID(), version); } wsrep_uuid_t node1{{1, }}; wsrep_uuid_t node2{{2, }}; wsrep_conn_id_t conn1{1}; wsrep_conn_id_t conn2{2}; wsrep_trx_id_t cur_trx_id{0}; wsrep_seqno_t cur_seqno{0}; struct CfCertResult { CertResult result; galera::TrxHandleSlavePtr ts; }; CfCertResult append(const wsrep_uuid_t& node, wsrep_conn_id_t conn, wsrep_seqno_t last_seen, const std::vector& key, wsrep_key_type_t type, int flags, const gu::byte_t* data_buf, size_t data_buf_len) { galera::TrxHandleMasterPtr txm{ galera::TrxHandleMaster::New( mp, galera::TrxHandleMaster::Params{ "", version, galera::KeySet::MAX_VERSION }, node, conn, cur_trx_id), galera::TrxHandleMasterDeleter{} }; txm->set_flags(flags); TestKey tkey{ txm->version(), type, key }; txm->append_key(tkey()); if (data_buf) { txm->append_data(data_buf, data_buf_len, WSREP_DATA_ORDERED, false); } galera::WriteSetNG::GatherVector out; size_t size = txm->write_set_out().gather( txm->source_id(), txm->conn_id(), txm->trx_id(), out); txm->finalize(last_seen); gu::byte_t* buf = static_cast(gcache.malloc(size)); ck_assert(out.serialize(buf, size) == size); ++cur_seqno; gcs_action act = { cur_seqno, cur_seqno, buf, static_cast(size), GCS_ACT_WRITESET }; galera::TrxHandleSlavePtr ts(galera::TrxHandleSlave::New(false, sp), galera::TrxHandleSlaveDeleter{}); ck_assert(ts->unserialize(act) == size); auto result = cert.append_trx(ts); /* Mark committed here to avoid doing it in every test case. If the * ts is not marked as committed, the certification destructor will * assert during cleanup. */ ts->mark_committed(); return { result, ts }; } CfCertResult append_trx(const wsrep_uuid_t& node, wsrep_conn_id_t conn, wsrep_seqno_t last_seen, const std::vector& key, wsrep_key_type_t type) { return append(node, conn, last_seen, key, type, galera::TrxHandle::F_BEGIN | galera::TrxHandle::F_COMMIT, nullptr, 0); } CfCertResult append_toi(const wsrep_uuid_t& node, wsrep_conn_id_t conn, wsrep_seqno_t last_seen, const std::vector& key, wsrep_key_type_t type) { return append(node, conn, last_seen, key, type, galera::TrxHandle::F_BEGIN | galera::TrxHandle::F_COMMIT | galera::TrxHandle::F_ISOLATION, nullptr, 0); } CfCertResult append_nbo_begin(const wsrep_uuid_t& node, wsrep_conn_id_t conn, wsrep_seqno_t last_seen, const std::vector& key, wsrep_key_type_t type) { return append(node, conn, last_seen, key, type, galera::TrxHandle::F_BEGIN | galera::TrxHandle::F_ISOLATION, nullptr, 0); } CfCertResult append_nbo_end(const wsrep_uuid_t& node, wsrep_conn_id_t conn, wsrep_seqno_t last_seen, const std::vector& key, wsrep_key_type_t type, wsrep_seqno_t begin_seqno) { gu::byte_t buf[24]; galera::NBOKey nbo_key(begin_seqno); size_t nbo_key_len = nbo_key.serialize(buf, sizeof(buf), 0); return append(node, conn, last_seen, key, type, galera::TrxHandle::F_COMMIT | galera::TrxHandle::F_ISOLATION, buf, nbo_key_len); } }; /* This testcase is mainly for checking that the CertFixture works correctly. */ START_TEST(cert_append_trx) { CertFixture f; auto res = f.append_trx(f.node1, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert(res.ts->certified()); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); ck_assert_int_eq(res.ts->global_seqno(), 1); } END_TEST /* * Cert against shared */ START_TEST(cert_certify_shared_shared) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); } END_TEST START_TEST(cert_certify_shared_reference) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); } END_TEST START_TEST(cert_certify_shared_update) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_shared_exclusive) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* * Cert against reference */ START_TEST(cert_certify_reference_shared) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); } END_TEST START_TEST(cert_certify_reference_reference) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); } END_TEST START_TEST(cert_certify_reference_update) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_reference_exclusive) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* * Cert against update */ START_TEST(cert_certify_update_shared) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_update_reference) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_update_update) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_update_exclusive) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* * Cert against exclusive */ START_TEST(cert_certify_exclusive_shared) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_exclusive_reference) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_exclusive_update) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_exclusive_exclusive) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* * Certify branch against leaf. In these cases the first write set has 2 key * parts, the second 3 so that the second write set branch key certifies against * first write set leaf. These are not actually tests for certification, * but rather for key appending producing proper branch keys. * Also, in these tests the leaf key for the second transaction does not matter. */ START_TEST(cert_certify_shared_branch) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, { "b", "b" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); } END_TEST START_TEST(cert_certify_reference_branch) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, { "b", "b" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); } END_TEST START_TEST(cert_certify_update_branch) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, { "b", "b" }, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_exclusive_branch) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, { "b", "b" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* Test certification for branch against other key types. */ START_TEST(cert_certify_branch_shared) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, { "b", "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "b" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); } END_TEST START_TEST(cert_certify_branch_reference) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, { "b", "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "b" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); } END_TEST START_TEST(cert_certify_branch_update) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, { "b", "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "b" }, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_branch_exclusive) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, { "b", "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "b" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* * TOI shared */ START_TEST(cert_certify_toi_shared_shared) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); } END_TEST START_TEST(cert_certify_toi_shared_reference) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); } END_TEST START_TEST(cert_certify_toi_shared_update) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_toi_shared_exclusive) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* * TOI reference */ START_TEST(cert_certify_toi_reference_shared) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); } END_TEST START_TEST(cert_certify_toi_reference_reference) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); } END_TEST START_TEST(cert_certify_toi_reference_update) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_toi_reference_exclusive) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* * TOI update */ START_TEST(cert_certify_toi_update_shared) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_toi_update_reference) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_toi_update_update) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_toi_update_exclusive) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* * TOI exclusive */ START_TEST(cert_certify_toi_exclusive_shared) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_toi_exclusive_reference) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_REFERENCE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_toi_exclusive_update) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_UPDATE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_toi_exclusive_exclusive) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* Exclusive - exclusive TOI to demonstrate that TOI never fails * in certification. */ START_TEST(cert_certify_exclusive_toi_exclusive) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_toi(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* Exclusive TOI - Exclusive TOI */ START_TEST(cert_certify_exclusive_toi_exclusive_toi) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_toi(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* NBO begin - TOI */ START_TEST(cert_certify_exclusive_nbo_exclusive_toi) { CertFixture f; auto res = f.append_nbo_begin(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->global_seqno(), 1); res = f.append_toi(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); res = f.append_nbo_end(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE, 1); res = f.append_toi(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 3); } END_TEST /* TOI - NBO begin */ START_TEST(cert_certify_exclusive_toi_exclusive_nbo) { CertFixture f; auto res = f.append_toi(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_nbo_begin(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->global_seqno(), 2); ck_assert_int_eq(res.ts->depends_seqno(), 1); res = f.append_nbo_end(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE, 2); res = f.append_toi(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 3); } END_TEST /* NBO begin - NBO begin*/ START_TEST(cert_certify_exclusive_nbo_exclusive_nbo) { CertFixture f; auto res = f.append_nbo_begin(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->global_seqno(), 1); res = f.append_nbo_begin(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); ck_assert_int_eq(res.ts->depends_seqno(), 1); res = f.append_nbo_end(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE, 1); res = f.append_nbo_begin(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 3); } END_TEST /* Write sets originating from the same node should not conflict even with * exclusive key. */ START_TEST(cert_certify_same_node) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node1, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* Write set outside certification range must not cause conflict, but dependency. */ START_TEST(cert_certify_exclusive_exclusive_outside_cert_range) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 1, {"b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_exclusive_exclusive_shadowed_by_shared) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 1, {"b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); res = f.append_trx(f.node2, f.conn2, 0, {"b", "l"}, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_FAILED); /* Note that even though the dependency should be to shared key, the * certification checks first for exclusive key and because of conflict, * the scan stops there and the depends seqno is not updated. This does * not matter however, as the test result is failed. */ ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* Even though shared-shared match does not cause conflict or dependency, * having PA_UNSAFE flag in write set must create the dependency. */ START_TEST(cert_certify_shared_shared_pa_unsafe) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "l"}, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_SHARED, galera::TrxHandle::F_BEGIN | galera::TrxHandle::F_COMMIT | galera::TrxHandle::F_PA_UNSAFE, nullptr, 0); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST /* PA unsafe must create dependency even if there is no match. */ START_TEST(cert_certify_no_match_pa_unsafe) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, {"b", "m"}, WSREP_KEY_SHARED); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_SHARED, galera::TrxHandle::F_BEGIN | galera::TrxHandle::F_COMMIT | galera::TrxHandle::F_PA_UNSAFE, nullptr, 0); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 1); } END_TEST START_TEST(cert_certify_no_match) { CertFixture f; auto res = f.append_trx(f.node1, f.conn1, 0, { "b", "m" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); res = f.append_trx(f.node2, f.conn2, 0, { "b", "l" }, WSREP_KEY_EXCLUSIVE); ck_assert_int_eq(res.result, CertResult::TEST_OK); ck_assert_int_eq(res.ts->depends_seqno(), 0); } END_TEST Suite* certification_suite() { Suite* s(suite_create("certification")); TCase* t; t = tcase_create("certification_trx_v4"); tcase_add_test(t, test_certification_trx_v4); suite_add_tcase(s, t); t = tcase_create("certification_toi_v3"); tcase_add_test(t, test_certification_toi_v3); suite_add_tcase(s, t); t = tcase_create("certification_trx_different_level_v4"); tcase_add_test(t, test_certification_trx_different_level_v4); suite_add_tcase(s, t); t = tcase_create("certification_nbo"); tcase_add_test(t, test_certification_nbo); suite_add_tcase(s, t); t = tcase_create("certification_commit_fragment"); tcase_add_test(t, test_certification_commit_fragment); suite_add_tcase(s, t); t = tcase_create("certification_zero_level"); tcase_add_test(t, test_certification_zero_level); suite_add_tcase(s, t); t = tcase_create("certification_rules"); tcase_add_test(t, cert_append_trx); tcase_add_test(t, cert_certify_shared_shared); tcase_add_test(t, cert_certify_shared_reference); tcase_add_test(t, cert_certify_shared_update); tcase_add_test(t, cert_certify_shared_exclusive); tcase_add_test(t, cert_certify_reference_shared); tcase_add_test(t, cert_certify_reference_reference); tcase_add_test(t, cert_certify_reference_update); tcase_add_test(t, cert_certify_reference_exclusive); tcase_add_test(t, cert_certify_update_shared); tcase_add_test(t, cert_certify_update_reference); tcase_add_test(t, cert_certify_update_update); tcase_add_test(t, cert_certify_update_exclusive); tcase_add_test(t, cert_certify_exclusive_shared); tcase_add_test(t, cert_certify_exclusive_reference); tcase_add_test(t, cert_certify_exclusive_update); tcase_add_test(t, cert_certify_exclusive_exclusive); tcase_add_test(t, cert_certify_shared_branch); tcase_add_test(t, cert_certify_reference_branch); tcase_add_test(t, cert_certify_update_branch); tcase_add_test(t, cert_certify_exclusive_branch); tcase_add_test(t, cert_certify_branch_shared); tcase_add_test(t, cert_certify_branch_reference); tcase_add_test(t, cert_certify_branch_update); tcase_add_test(t, cert_certify_branch_exclusive); tcase_add_test(t, cert_certify_toi_shared_shared); tcase_add_test(t, cert_certify_toi_shared_reference); tcase_add_test(t, cert_certify_toi_shared_update); tcase_add_test(t, cert_certify_toi_shared_exclusive); tcase_add_test(t, cert_certify_toi_reference_shared); tcase_add_test(t, cert_certify_toi_reference_reference); tcase_add_test(t, cert_certify_toi_reference_update); tcase_add_test(t, cert_certify_toi_reference_exclusive); tcase_add_test(t, cert_certify_toi_update_shared); tcase_add_test(t, cert_certify_toi_update_reference); tcase_add_test(t, cert_certify_toi_update_update); tcase_add_test(t, cert_certify_toi_update_exclusive); tcase_add_test(t, cert_certify_toi_exclusive_shared); tcase_add_test(t, cert_certify_toi_exclusive_reference); tcase_add_test(t, cert_certify_toi_exclusive_update); tcase_add_test(t, cert_certify_toi_exclusive_exclusive); tcase_add_test(t, cert_certify_exclusive_toi_exclusive); tcase_add_test(t, cert_certify_exclusive_toi_exclusive_toi); tcase_add_test(t, cert_certify_exclusive_nbo_exclusive_toi); tcase_add_test(t, cert_certify_exclusive_toi_exclusive_nbo); tcase_add_test(t, cert_certify_exclusive_nbo_exclusive_nbo); tcase_add_test(t, cert_certify_same_node); tcase_add_test(t, cert_certify_exclusive_exclusive_outside_cert_range); tcase_add_test(t, cert_certify_exclusive_exclusive_shadowed_by_shared); tcase_add_test(t, cert_certify_shared_shared_pa_unsafe); tcase_add_test(t, cert_certify_no_match_pa_unsafe); tcase_add_test(t, cert_certify_no_match); suite_add_tcase(s, t); return s; } galera-4-26.4.22/galera/tests/write_set_ng_check.cpp000644 000162 177776 00000027420 14755062442 023446 0ustar00jenkinsnogroup000000 000000 /* Copyright (C) 2013-2024 Codership Oy * * $Id$ */ #undef NDEBUG #include "test_key.hpp" #include "../src/write_set_ng.hpp" #include "gu_uuid.h" #include "gu_logger.hpp" #include "gu_hexdump.hpp" #include "gu_inttypes.hpp" #include using namespace galera; static void ver3_basic(gu::RecordSet::Version const rsv, WriteSetNG::Version const wsv) { int const alignment(rsv >= gu::RecordSet::VER2 ? GU_MIN_ALIGNMENT : 1); uint16_t const flag1(0xabcd); wsrep_uuid_t source; gu_uuid_generate (reinterpret_cast(&source), NULL, 0); wsrep_conn_id_t const conn(652653); wsrep_trx_id_t const trx(99994952); std::string const dir("."); wsrep_trx_id_t trx_id(1); WriteSetOut wso (dir, trx_id, KeySet::FLAT8A, 0, 0, flag1, rsv, wsv); ck_assert(wso.is_empty()); // keep WSREP_KEY_SHARED here, see loop below TestKey tk0(KeySet::MAX_VERSION, WSREP_KEY_SHARED, true, "a0", "a1"); wso.append_key(tk0()); int const expected_count(2); ck_assert(wso.is_empty() == false); uint64_t const data_out_volatile(0xaabbccdd); uint32_t const data_out_persistent(0xffeeddcc); uint16_t const flag2(0x1234); { uint64_t const d(data_out_volatile); wso.append_data (&d, sizeof(d), true); } wso.append_data (&data_out_persistent, sizeof(data_out_persistent), false); wso.add_flags (flag2); uint16_t const flags(flag1 | flag2); WriteSetNG::GatherVector out; size_t const out_size(wso.gather(source, conn, trx, out)); log_info << "Gather size: " << out_size << ", buf count: " << out->size(); ck_assert((out_size % alignment) == 0); wsrep_seqno_t const last_seen(1); wsrep_seqno_t const seqno(2); int const pa_range(seqno - last_seen); wso.finalize(last_seen, 0); /* concatenate all out buffers */ std::vector in; in.reserve(out_size); for (size_t i(0); i < out->size(); ++i) { const gu::byte_t* ptr(static_cast(out[i].ptr)); in.insert (in.end(), ptr, ptr + out[i].size); } ck_assert(in.size() == out_size); gu::Buf const in_buf = { in.data(), static_cast(in.size()) }; int const P_BRANCH(KeySet::KeyPart::prefix(KeyData::BRANCH_KEY_TYPE, wsv)); int const P_SHARED(KeySet::KeyPart::prefix(WSREP_KEY_SHARED, wsv)); /* read ws buffer and "certify" */ { mark_point(); WriteSetIn wsi(in_buf); mark_point(); wsi.verify_checksum(); wsrep_seqno_t const ls(wsi.last_seen()); ck_assert_msg(ls == last_seen, "Found last seen: %" PRId64 ", expected: %" PRId64, ls, last_seen); ck_assert(wsi.flags() == flags); ck_assert(0 != wsi.timestamp()); ck_assert(wsi.annotated() == false); mark_point(); const KeySetIn& ksi(wsi.keyset()); ck_assert(ksi.count() == expected_count); mark_point(); int branch(0); int shared(0); for (int i(0); i < ksi.count(); ++i) { KeySet::KeyPart kp(ksi.next()); branch += (kp.prefix() == P_BRANCH); shared += (kp.prefix() == P_SHARED); } ck_assert(branch > 0); ck_assert(shared > 0); wsi.verify_checksum(); wsi.set_seqno (seqno, pa_range); ck_assert_msg(wsi.pa_range() == pa_range, "wsi.pa_range = %d\n pa_range = %d", wsi.pa_range(), pa_range); ck_assert(wsi.certified()); } /* repeat reading buffer after "certification" */ { WriteSetIn wsi(in_buf); mark_point(); wsi.verify_checksum(); ck_assert(wsi.certified()); ck_assert(wsi.seqno() == seqno); ck_assert(wsi.flags() == (flags | WriteSetNG::F_CERTIFIED)); ck_assert(0 != wsi.timestamp()); mark_point(); const KeySetIn& ksi(wsi.keyset()); ck_assert(ksi.count() == expected_count); mark_point(); int branch(0); for (int i(0); i < ksi.count(); ++i) { KeySet::KeyPart kp(ksi.next()); branch += (kp.prefix() == P_BRANCH); } ck_assert(branch > 0); wsi.verify_checksum(); mark_point(); const DataSetIn& dsi(wsi.dataset()); ck_assert(dsi.count() == 1); mark_point(); gu::Buf const d(dsi.next()); ck_assert(d.size == sizeof(data_out_volatile) + sizeof(data_out_persistent)); const char* dptr = static_cast(d.ptr); ck_assert(*(reinterpret_cast(dptr)) == data_out_volatile); ck_assert(*(reinterpret_cast (dptr + sizeof(data_out_volatile))) == data_out_persistent); mark_point(); const DataSetIn& usi(wsi.unrdset()); ck_assert(usi.count() == 0); ck_assert(usi.size() == 0); } mark_point(); try /* this is to test checksum after set_seqno() */ { WriteSetIn wsi(in_buf); mark_point(); wsi.verify_checksum(); ck_assert(wsi.certified()); ck_assert(wsi.pa_range() == pa_range); ck_assert(wsi.seqno() == seqno); ck_assert(!memcmp(&wsi.source_id(), &source, sizeof(source))); ck_assert(wsi.conn_id() == conn); ck_assert(wsi.trx_id() == trx); } catch (gu::Exception& e) { ck_assert(e.get_errno() == EINVAL); } mark_point(); /* this is to test reassembly without keys and unordered data after gather() * + late initialization */ try { WriteSetIn tmp_wsi(in_buf); WriteSetIn::GatherVector out; mark_point(); tmp_wsi.verify_checksum(); gu_trace(tmp_wsi.gather(out, false, false)); // no keys or unrd /* concatenate all out buffers */ std::vector in; in.reserve(out_size); for (size_t i(0); i < out->size(); ++i) { const gu::byte_t* ptr (static_cast(out[i].ptr)); in.insert (in.end(), ptr, ptr + out[i].size); } mark_point(); gu::Buf tmp_buf = { in.data(), static_cast(in.size()) }; WriteSetIn wsi; // first - create an empty writeset wsi.read_buf(tmp_buf); // next - initialize from buffer mark_point(); wsi.verify_checksum(); ck_assert(wsi.certified()); ck_assert(wsi.pa_range() == pa_range); ck_assert(wsi.seqno() == seqno); ck_assert(wsi.keyset().count() == 0); ck_assert(wsi.dataset().count() != 0); ck_assert(wsi.unrdset().count() == 0); } catch (gu::Exception& e) { ck_assert(e.get_errno() == EINVAL); } in[in.size() - 1] ^= 1; // corrupted the last byte (payload) mark_point(); try /* this is to test payload corruption */ { WriteSetIn wsi(in_buf); mark_point(); wsi.verify_checksum(); ck_abort_msg("payload corruption slipped through 1"); } catch (gu::Exception& e) { ck_assert(e.get_errno() == EINVAL); } try /* this is to test background checksumming + corruption */ { WriteSetIn wsi(in_buf, 2); mark_point(); try { wsi.verify_checksum(); ck_abort_msg("payload corruption slipped through 2"); } catch (gu::Exception& e) { ck_assert(e.get_errno() == EINVAL); } } catch (std::exception& e) { ck_abort_msg("%s", e.what()); } in[2] ^= 1; // corrupted 3rd byte of header try /* this is to test header corruption */ { WriteSetIn wsi(in_buf, 2 /* this should postpone payload checksum */); wsi.verify_checksum(); ck_abort_msg("header corruption slipped through"); } catch (gu::Exception& e) { ck_assert(e.get_errno() == EINVAL); } } #ifndef GALERA_ONLY_ALIGNED START_TEST (ver3_basic_rsv1) { ver3_basic(gu::RecordSet::VER1, WriteSetNG::VER3); } END_TEST #endif /* GALERA_ONLY_ALIGNED */ START_TEST (ver3_basic_rsv2_wsv3) { ver3_basic(gu::RecordSet::VER2, WriteSetNG::VER3); } END_TEST START_TEST (ver3_basic_rsv2_wsv4) { ver3_basic(gu::RecordSet::VER2, WriteSetNG::VER4); } END_TEST static void ver3_annotation(gu::RecordSet::Version const rsv) { int const alignment(rsv >= gu::RecordSet::VER2 ? GU_MIN_ALIGNMENT : 1); uint16_t const flag1(0xabcd); wsrep_uuid_t source; gu_uuid_generate (reinterpret_cast(&source), NULL, 0); wsrep_conn_id_t const conn(652653); wsrep_trx_id_t const trx(99994952); std::string const dir("."); wsrep_trx_id_t trx_id(1); WriteSetOut wso (dir, trx_id, KeySet::FLAT16, 0, 0, flag1, rsv, WriteSetNG::VER3); ck_assert(wso.is_empty()); TestKey tk0(KeySet::MAX_VERSION, WSREP_KEY_SHARED, true, "key0"); wso.append_key(tk0()); ck_assert(wso.is_empty() == false); uint64_t const data(0xaabbccdd); std::string const annotation("0xaabbccdd"); uint16_t const flag2(0x1234); wso.append_data (&data, sizeof(data), true); wso.append_annotation (annotation.c_str(), annotation.size(), true); wso.add_flags (flag2); uint16_t const flags(flag1 | flag2); WriteSetNG::GatherVector out; size_t const out_size(wso.gather(source, conn, trx, out)); log_info << "Gather size: " << out_size << ", buf count: " << out->size(); ck_assert((out_size % alignment) == 0); ck_assert(out_size >= (sizeof(data) + annotation.size())); wsrep_seqno_t const last_seen(1); wso.finalize(last_seen, 0); /* concatenate all out buffers */ std::vector in; in.reserve(out_size); for (size_t i(0); i < out->size(); ++i) { const gu::byte_t* ptr(static_cast(out[i].ptr)); in.insert (in.end(), ptr, ptr + out[i].size); } ck_assert(in.size() == out_size); gu::Buf const in_buf = { in.data(), static_cast(in.size()) }; /* read buffer into WriteSetIn */ mark_point(); WriteSetIn wsi(in_buf); mark_point(); wsi.verify_checksum(); wsrep_seqno_t const ls(wsi.last_seen()); ck_assert_msg(ls == last_seen, "Found last seen: %" PRId64 ", expected: %" PRId64, ls, last_seen); ck_assert(wsi.flags() == flags); ck_assert(0 != wsi.timestamp()); ck_assert(wsi.annotated()); /* check that annotation has survived */ std::ostringstream os; wsi.write_annotation(os); std::string const res(os.str().c_str()); ck_assert_msg(annotation.length() == res.length(), "Initial ann. length: %zu, resulting ann.length: %zu", annotation.length(), res.length()); ck_assert_msg(annotation == res, "Initial annotation: '%s', resulting annotation: '%s'", annotation.c_str(), res.c_str()); } #ifndef GALERA_ONLY_ALIGNED START_TEST (ver3_annotation_rsv1) { ver3_annotation(gu::RecordSet::VER1); } END_TEST #endif /* GALERA_ONLY_ALIGNED */ START_TEST (ver3_annotation_rsv2) { ver3_annotation(gu::RecordSet::VER2); } END_TEST Suite* write_set_ng_suite () { Suite* s = suite_create ("WriteSet"); TCase* t = tcase_create ("WriteSet basic"); #ifndef GALERA_ONLY_ALIGNED tcase_add_test (t, ver3_basic_rsv1); #endif tcase_add_test (t, ver3_basic_rsv2_wsv3); tcase_add_test (t, ver3_basic_rsv2_wsv4); tcase_set_timeout(t, 60); suite_add_tcase (s, t); t = tcase_create ("WriteSet annotation"); #ifndef GALERA_ONLY_ALIGNED tcase_add_test (t, ver3_annotation_rsv1); #endif tcase_add_test (t, ver3_annotation_rsv2); tcase_set_timeout(t, 60); suite_add_tcase (s, t); return s; } galera-4-26.4.22/galera/tests/galera_check.cpp000644 000162 177776 00000003353 14755062442 022207 0ustar00jenkinsnogroup000000 000000 /* * Copyright (C) 2012-2018 Codership Oy */ #include #include #include #include /* * Suite descriptions: forward-declare and add to array */ typedef Suite* (*suite_creator_t) (void); extern Suite* data_set_suite(); extern Suite* key_set_suite(); extern Suite* write_set_ng_suite(); extern Suite* certification_suite(); //extern Suite* write_set_suite(); extern Suite* trx_handle_suite(); extern Suite* service_thd_suite(); extern Suite* ist_suite(); extern Suite* saved_state_suite(); extern Suite* defaults_suite(); extern Suite* progress_suite(); static suite_creator_t suites[] = { data_set_suite, key_set_suite, write_set_ng_suite, certification_suite, trx_handle_suite, service_thd_suite, ist_suite, saved_state_suite, defaults_suite, progress_suite, 0 }; extern "C" { #include } #define LOG_FILE "galera_check.log" int main(int argc, char* argv[]) { bool no_fork = (argc >= 2 && std::string(argv[1]) == "nofork"); FILE* log_file = 0; if (!no_fork) { log_file = fopen (LOG_FILE, "w"); if (!log_file) return EXIT_FAILURE; gu_conf_set_log_file (log_file); } gu_conf_debug_on(); int failed = 0; for (int i = 0; suites[i] != 0; ++i) { SRunner* sr = srunner_create(suites[i]()); if (no_fork) srunner_set_fork_status(sr, CK_NOFORK); srunner_run_all(sr, CK_NORMAL); failed += srunner_ntests_failed(sr); srunner_free(sr); } if (log_file != 0) fclose(log_file); printf ("Total tests failed: %d\n", failed); if (0 == failed && 0 != log_file) ::unlink(LOG_FILE); return failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } galera-4-26.4.22/galera/CMakeLists.txt000644 000162 177776 00000000152 14755062442 020503 0ustar00jenkinsnogroup000000 000000 # # Copyright (C) 2020 Codership Oy # add_subdirectory(src) add_subdirectory(tests) galera-4-26.4.22/galera/src/000755 000162 177776 00000000000 14755062445 016537 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/galera/src/mapped_buffer.cpp000644 000162 177776 00000007600 14755062442 022042 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010 Codership Oy // #define _FILE_OFFSET_BITS 64 #include "mapped_buffer.hpp" #include "gu_throw.hpp" #include "gu_logger.hpp" #include "gu_macros.h" #include #include #include #include #include // MAP_FAILED is defined as (void *) -1 #pragma GCC diagnostic ignored "-Wold-style-cast" using namespace std; using namespace gu; galera::MappedBuffer::MappedBuffer(const std::string& working_dir, size_t threshold) : working_dir_ (working_dir), file_ (), fd_ (-1), threshold_ (threshold), buf_ (0), buf_size_ (0), real_buf_size_(0) { } galera::MappedBuffer::~MappedBuffer() { if (fd_ != -1) { struct stat st; fstat(fd_, &st); log_debug << "file size " << st.st_size; } clear(); } void galera::MappedBuffer::reserve(size_t sz) { if (real_buf_size_ >= sz) { // no need for reallocation return; } if (sz > threshold_) { // buffer size exceeds in-memory threshold, have to mmap if (gu_unlikely(std::numeric_limits::max() - sz < threshold_)) { sz = std::numeric_limits::max(); } else { sz = (sz/threshold_ + 1)*threshold_; } if (gu_unlikely(sz > static_cast(std::numeric_limits::max()))) { gu_throw_error(EINVAL) << "size exceeds maximum of off_t"; } if (fd_ == -1) { file_ = working_dir_ + "/gmb_XXXXXX"; fd_ = mkstemp(&file_[0]); if (fd_ == -1) { gu_throw_system_error(errno) << "mkstemp(" << file_ << ") failed"; } if (ftruncate(fd_, sz) == -1) { gu_throw_system_error(errno) << "ftruncate() failed"; } byte_t* tmp(reinterpret_cast( mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd_, 0))); if (tmp == MAP_FAILED) { const int error = errno; free(buf_); buf_ = 0; clear(); gu_throw_system_error(error) << "mmap() failed"; } copy(buf_, buf_ + buf_size_, tmp); free(buf_); buf_ = tmp; } else { if (munmap(buf_, real_buf_size_) != 0) { gu_throw_system_error(errno) << "munmap() failed"; } if (ftruncate(fd_, sz) == -1) { gu_throw_system_error(errno) << "fruncate() failed"; } byte_t* tmp(reinterpret_cast( mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd_, 0))); if (tmp == MAP_FAILED) { const int error = errno; buf_ = 0; clear(); gu_throw_system_error(error) << "mmap() failed"; } buf_ = tmp; } } else { sz = min(threshold_, sz*2); byte_t* tmp(reinterpret_cast(realloc(buf_, sz))); if (tmp == 0) { gu_throw_system_error(ENOMEM) << "realloc failed"; } buf_ = tmp; } real_buf_size_ = sz; } void galera::MappedBuffer::resize(size_t sz) { reserve(sz); buf_size_ = sz; } void galera::MappedBuffer::clear() { if (fd_ != -1) { if (buf_ != 0) munmap(buf_, real_buf_size_); while (close(fd_) == EINTR) { } unlink(file_.c_str()); } else { free(buf_); } fd_ = -1; buf_ = 0; buf_size_ = 0; real_buf_size_ = 0; } galera-4-26.4.22/galera/src/galera_view.hpp000644 000162 177776 00000002343 14755062442 021534 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2015 Codership Oy // /*! @file galera_view.hpp * * Helper class and methods for manipulating views in galera code. */ #ifndef GALERA_VIEW_HPP #define GALERA_VIEW_HPP #include "wsrep_api.h" // for wsrep_view_info_t #include "gu_uuid.hpp" #include static inline bool operator<(const wsrep_uuid_t& lhs, const wsrep_uuid_t& rhs) { return (memcmp(lhs.data, rhs.data, sizeof(lhs.data)) < 0); } namespace galera { class View { public: class UUIDCmp { public: bool operator()(const wsrep_uuid_t& lhs, const wsrep_uuid_t& rhs) const { return (lhs < rhs); } }; // Convenience typedef for member set typedef std::set MembSet; // Default constructor View(); // Construct View from wsrep_view_info_t View(const wsrep_view_info_t&); // Destructor ~View(); // Return true if the members of the view are subset of // other MembSet. bool subset_of(const MembSet& other) const; private: MembSet members_; // members set }; } #endif // GALERA_VIEW_HPP galera-4-26.4.22/galera/src/gcs_action_source.hpp000644 000162 177776 00000003750 14755062442 022743 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2020 Codership Oy // #ifndef GALERA_GCS_ACTION_SOURCE_HPP #define GALERA_GCS_ACTION_SOURCE_HPP #include "action_source.hpp" #include "galera_gcs.hpp" #ifndef NDEBUG #include "replicator.hpp" #define REPL_IMPL Replicator #else #include "replicator_smm.hpp" #define REPL_IMPL ReplicatorSMM #endif #include "trx_handle.hpp" #include "GCache.hpp" #include "gu_atomic.hpp" namespace galera { class GcsActionSource : public galera::ActionSource { public: /* to be returned in case of inconsistency event */ static int const INCONSISTENCY_CODE = -ENOTRECOVERABLE; GcsActionSource(TrxHandleSlave::Pool& sp, GCS_IMPL& gcs, REPL_IMPL& replicator, gcache::GCache& gcache) : trx_pool_ (sp ), gcs_ (gcs ), replicator_ (replicator), gcache_ (gcache ), received_ (0 ), received_bytes_(0 ) { } ~GcsActionSource() { log_info << trx_pool_; } ssize_t process(void*, bool& exit_loop); long long received() const { return received_(); } long long received_bytes() const { return received_bytes_(); } private: void process_writeset(void* recv_ctx, const struct gcs_action& act, bool& exit_loop); void resend_writeset(const struct gcs_action& act); void dispatch(void*, const gcs_action&, bool& exit_loop); TrxHandleSlave::Pool& trx_pool_; GCS_IMPL& gcs_; REPL_IMPL& replicator_; gcache::GCache& gcache_; gu::Atomic received_; gu::Atomic received_bytes_; }; } #endif // GALERA_GCS_ACTION_SOURCE_HPP galera-4-26.4.22/galera/src/ist_proto.cpp000644 000162 177776 00000001324 14755062442 021262 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2015 Codership Oy // #include "ist_proto.hpp" std::ostream& galera::ist::operator<< (std::ostream& os, const Message& m) { os << "ver: " << m.version() << ", type: " << m.type() << ", flags: " << m.flags() << ", ctrl: " << m.ctrl() << ", len: " << m.len() << ", seqno: " << m.seqno(); return os; } void galera::ist::Message::throw_invalid_version(uint8_t const v) { gu_throw_error(EPROTO) << "invalid protocol version " << int(v) << ", expected " << int(version_); } void galera::ist::Message::throw_corrupted_header() { gu_throw_error(EINVAL) << "Corrupted IST message header: " << *this; } galera-4-26.4.22/galera/src/wsrep_params.cpp000644 000162 177776 00000004041 14755062442 021742 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2014 Codership Oy // #include "wsrep_params.hpp" #include "gu_dbug.h" #include "gu_debug_sync.hpp" void wsrep_set_params (galera::Replicator& repl, const char* params) { if (!params) return; std::vector > pv; gu::Config::parse (pv, params); for (size_t i(0); i < pv.size(); ++i) { const std::string& key(pv[i].first); const std::string& value(pv[i].second); try { if (key == galera::Replicator::Param::debug_log) { bool val(gu::from_string(value)); if (val == true) { gu_conf_debug_on(); } else { gu_conf_debug_off(); } } #ifdef GU_DBUG_ON else if (key == galera::Replicator::Param::dbug) { if (value.empty()) { GU_DBUG_POP(); } else { GU_DBUG_PUSH(value.c_str()); } } else if (key == galera::Replicator::Param::signal) { gu_debug_sync_signal(value); } #endif /* GU_DBUG_ON */ else { log_debug << "Setting param '" << key << "' = '" << value << '\''; repl.param_set(key, value); } } catch (gu::NotFound&) { log_warn << "Unknown parameter '" << key << "'"; gu_throw_error(EINVAL) << "Unknown parameter' " << key << "'"; } catch (gu::Exception& e) { log_warn << "Setting parameter '" << key << "' to '" << value << "' failed: " << e.what(); throw; } } } char* wsrep_get_params(const galera::Replicator& repl) { std::ostringstream os; os << repl.params(); return strdup(os.str().c_str()); } galera-4-26.4.22/galera/src/galera_gcs.hpp000644 000162 177776 00000034500 14755062442 021336 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2021 Codership Oy // #ifndef GALERA_GCS_HPP #define GALERA_GCS_HPP #include "write_set_ng.hpp" #include "wsrep_api.h" #include "gcs.hpp" #include #include #include #include #include #include // for gu::Mutex and gu::Cond #include #include #define GCS_IMPL Gcs namespace galera { class GcsI { public: GcsI() {} virtual ~GcsI() {} virtual ssize_t connect(const std::string& cluster_name, const std::string& cluster_url, bool bootstrap) = 0; virtual ssize_t set_initial_position(const gu::GTID& gtid) = 0; virtual void close() = 0; virtual ssize_t recv(gcs_action& act) = 0; typedef WriteSetNG::GatherVector WriteSetVector; virtual ssize_t sendv(const WriteSetVector&, size_t, gcs_act_type_t, bool, bool) = 0; virtual ssize_t send (const void*, size_t, gcs_act_type_t, bool) = 0; virtual ssize_t replv(const WriteSetVector&, gcs_action& act, bool, const wsrep_seq_cb_t*) = 0; virtual ssize_t repl(gcs_action& act, bool) = 0; virtual void caused(gu::GTID& gtid, gu::datetime::Date& wait_until) = 0; virtual ssize_t schedule() = 0; virtual ssize_t interrupt(ssize_t) = 0; virtual ssize_t resume_recv() = 0; virtual ssize_t request_state_transfer(int version, const void* req, ssize_t req_len, const std::string& sst_donor, const gu::GTID& ist_gtid, gcs_seqno_t& order) = 0; virtual ssize_t desync(gcs_seqno_t& seqno_l) = 0; virtual void join(const gu::GTID&, int code) = 0; virtual gcs_seqno_t local_sequence() = 0; virtual ssize_t set_last_applied(const gu::GTID&) = 0; virtual int vote(const gu::GTID& gtid, uint64_t code, const void* data, size_t data_len) = 0; virtual void get_stats(gcs_stats*) const = 0; virtual void flush_stats() = 0; virtual void get_status(gu::Status&) const = 0; /*! @throws NotFound */ virtual void param_set (const std::string& key, const std::string& value) = 0; /*! @throws NotFound */ virtual char* param_get (const std::string& key) const = 0; virtual size_t max_action_size() const = 0; }; class Gcs : public GcsI { public: Gcs(gu::Config& config, gcache::GCache& cache, gu::Progress::Callback* cb, int repl_proto_ver = 0, int appl_proto_ver = 0, const char* node_name = 0, const char* node_incoming = 0) : conn_(gcs_create(config, reinterpret_cast(&cache), cb, node_name, node_incoming, repl_proto_ver, appl_proto_ver)) { log_info << "Passing config to GCS: " << config; if (conn_ == 0) gu_throw_fatal << "could not create gcs connection"; } ~Gcs() { gcs_destroy(conn_); } ssize_t connect(const std::string& cluster_name, const std::string& cluster_url, bool const bootstrap) { return gcs_open(conn_, cluster_name.c_str(), cluster_url.c_str(), bootstrap); } ssize_t set_initial_position(const gu::GTID& gtid) { return gcs_init(conn_, gtid); } void close() { gcs_close(conn_); } ssize_t recv(struct gcs_action& act) { return gcs_recv(conn_, &act); } ssize_t sendv(const WriteSetVector& actv, size_t act_len, gcs_act_type_t act_type, bool scheduled, bool grab) { return gcs_sendv(conn_, &actv[0], act_len, act_type, scheduled, grab); } ssize_t send(const void* act, size_t act_len, gcs_act_type_t act_type, bool scheduled) { return gcs_send(conn_, act, act_len, act_type, scheduled); } ssize_t replv(const WriteSetVector& actv, struct gcs_action& act, bool scheduled, const wsrep_seq_cb_t* seq_cb) { return gcs_replv(conn_, &actv[0], &act, scheduled, seq_cb); } ssize_t repl(struct gcs_action& act, bool scheduled) { return gcs_repl(conn_, &act, scheduled); } void caused(gu::GTID& gtid, gu::datetime::Date& wait_until) { long err; while ((err = gcs_caused(conn_, gtid)) == -EAGAIN && gu::datetime::Date::calendar() < wait_until) { usleep(1000); } if (err == -EAGAIN) err = -ETIMEDOUT; if (err < 0) { gu_throw_error(-err); } } ssize_t schedule() { return gcs_schedule(conn_); } ssize_t interrupt(ssize_t handle) { return gcs_interrupt(conn_, handle); } ssize_t resume_recv() { return gcs_resume_recv(conn_); } ssize_t set_last_applied(const gu::GTID& gtid) { assert(gtid.uuid() != GU_UUID_NIL); assert(gtid.seqno() >= 0); return gcs_set_last_applied(conn_, gtid); } int vote(const gu::GTID& gtid, uint64_t const code, const void* const msg, size_t const msg_len) { assert(gtid.uuid() != GU_UUID_NIL); assert(gtid.seqno() >= 0); return gcs_vote(conn_, gtid, code, msg, msg_len); } ssize_t request_state_transfer(int version, const void* req, ssize_t req_len, const std::string& sst_donor, const gu::GTID& ist_gtid, gcs_seqno_t& seqno_l) { return gcs_request_state_transfer(conn_, version, req, req_len, sst_donor.c_str(), ist_gtid, seqno_l); } ssize_t desync (gcs_seqno_t& seqno_l) { return gcs_desync(conn_, seqno_l); } void join (const gu::GTID& gtid, int const code) { long const err(gcs_join(conn_, gtid, code)); if (err < 0) { gu_throw_error (-err) << "gcs_join(" << gtid << ") failed"; } } gcs_seqno_t local_sequence() { return gcs_local_sequence(conn_); } void get_stats(gcs_stats* stats) const { return gcs_get_stats(conn_, stats); } void flush_stats() { return gcs_flush_stats(conn_); } void get_status(gu::Status& status) const { gcs_get_status(conn_, status); } void param_set (const std::string& key, const std::string& value) { long ret = gcs_param_set (conn_, key.c_str(), value.c_str()); if (1 == ret) { throw gu::NotFound(); } else if (ret) { gu_throw_error(-ret) << "Setting '" << key << "' to '" << value << "' failed"; } } char* param_get (const std::string& key) const { gu_throw_error(ENOSYS) << "Not implemented: " << __FUNCTION__; return 0; } size_t max_action_size() const { return GCS_MAX_ACT_SIZE; } private: Gcs(const Gcs&); void operator=(const Gcs&); gcs_conn_t* conn_; }; class DummyGcs : public GcsI { public: DummyGcs(gu::Config& config, gcache::GCache& cache, int repl_proto_ver = 0, int appl_proto_ver = 0, const char* node_name = 0, const char* node_incoming = 0); DummyGcs(); // for unit tests ~DummyGcs(); ssize_t connect(const std::string& cluster_name, const std::string& cluster_url, bool bootstrap); ssize_t set_initial_position(const gu::GTID& gtid); void close(); ssize_t recv(gcs_action& act); ssize_t sendv(const WriteSetVector&, size_t, gcs_act_type_t, bool, bool) { return -ENOSYS; } ssize_t send(const void*, size_t, gcs_act_type_t, bool) { return -ENOSYS; } ssize_t replv(const WriteSetVector& actv, gcs_action& act, bool scheduled, const wsrep_seq_cb_t*) { ssize_t ret(set_seqnos(act)); if (gu_likely(0 != gcache_ && ret > 0)) { assert (ret == act.size); gu::byte_t* ptr( reinterpret_cast(gcache_->malloc(act.size))); act.buf = ptr; ssize_t copied(0); for (int i(0); copied < act.size; ++i) { memcpy (ptr + copied, actv[i].ptr, actv[i].size); copied += actv[i].size; } assert (copied == act.size); } return ret; } ssize_t repl(gcs_action& act, bool scheduled) { ssize_t ret(set_seqnos(act)); if (gu_likely(0 != gcache_ && ret > 0)) { assert (ret == act.size); void* const ptr(gcache_->malloc(act.size)); memcpy (ptr, act.buf, act.size); act.buf = ptr; // no freeing here - initial act.buf belongs to the caller } return ret; } void caused(gu::GTID& gtid, gu::datetime::Date& wait_until) { gtid.set(uuid_, global_seqno_); } ssize_t schedule() { return 1; } ssize_t interrupt(ssize_t handle); ssize_t resume_recv() { return 0; } ssize_t set_last_applied(const gu::GTID& gtid) { gu::Lock lock(mtx_); last_applied_ = gtid.seqno(); report_last_applied_ = true; cond_.signal(); return 0; } gcs_seqno_t last_applied() const { return last_applied_; } int vote(const gu::GTID& gtid, uint64_t const code, const void* const msg, size_t const msg_len) { return 0; // we always agree with ourselves } ssize_t request_state_transfer(int version, const void* req, ssize_t req_len, const std::string& sst_donor, const gu::GTID& ist_gtid, gcs_seqno_t& seqno_l) { seqno_l = GCS_SEQNO_ILL; return -ENOSYS; } ssize_t desync (gcs_seqno_t& seqno_l) { seqno_l = GCS_SEQNO_ILL; return -ENOTCONN; } void join(const gu::GTID& gtid, int const code) { gu_throw_error(ENOTCONN); } gcs_seqno_t local_sequence() { gu::Lock lock(mtx_); return ++local_seqno_; } void get_stats(gcs_stats* stats) const { memset (stats, 0, sizeof(*stats)); } void flush_stats() {} void get_status(gu::Status& status) const {} void param_set (const std::string& key, const std::string& value) {} char* param_get (const std::string& key) const { return 0; } size_t max_action_size() const { return 0x7FFFFFFF; } private: typedef enum { S_CLOSED, S_OPEN, S_CONNECTED, S_SYNCED } conn_state_t; ssize_t generate_seqno_action (gcs_action& act, gcs_act_type_t type); ssize_t generate_cc (bool primary); gu::Config* gconf_; gcache::GCache* gcache_; gu::Mutex mtx_; gu::Cond cond_; gcs_seqno_t global_seqno_; gcs_seqno_t local_seqno_; gu::UUID uuid_; gcs_seqno_t last_applied_; conn_state_t state_; gu::Lock* schedule_; void* cc_; ssize_t cc_size_; std::string const my_name_; std::string const incoming_; int repl_proto_ver_; int appl_proto_ver_; bool report_last_applied_; ssize_t set_seqnos (gcs_action& act) { act.seqno_g = GCS_SEQNO_ILL; act.seqno_l = GCS_SEQNO_ILL; ssize_t ret(-EBADFD); { gu::Lock lock(mtx_); switch (state_) { case S_CONNECTED: case S_SYNCED: { ++global_seqno_; act.seqno_g = global_seqno_; ++local_seqno_; act.seqno_l = local_seqno_; ret = act.size; break; } case S_CLOSED: ret = -EBADFD; break; case S_OPEN: ret = -ENOTCONN; break; } } return ret; } DummyGcs (const DummyGcs&); DummyGcs& operator=(const DummyGcs&); }; } #endif // GALERA_GCS_HPP galera-4-26.4.22/galera/src/ist.cpp000644 000162 177776 00000055326 14755062442 020052 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2011-2021 Codership Oy // #include "ist.hpp" #include "ist_proto.hpp" #include "gu_logger.hpp" #include "gu_uri.hpp" #include "gu_debug_sync.hpp" #include "gu_progress.hpp" #include "galera_common.hpp" #include #include #include namespace { static std::string const CONF_KEEP_KEYS ("ist.keep_keys"); static bool const CONF_KEEP_KEYS_DEFAULT (true); } namespace galera { namespace ist { class AsyncSender : public Sender { public: AsyncSender(const gu::Config& conf, const std::string& peer, wsrep_seqno_t first, wsrep_seqno_t last, wsrep_seqno_t preload_start, AsyncSenderMap& asmap, int version) : Sender (conf, asmap.gcache(), peer, version), conf_ (conf), peer_ (peer), first_ (first), last_ (last), preload_start_(preload_start), asmap_ (asmap), thread_() { } const gu::Config& conf() { return conf_; } const std::string& peer() const { return peer_; } wsrep_seqno_t first() const { return first_; } wsrep_seqno_t last() const { return last_; } wsrep_seqno_t preload_start() const { return preload_start_; } AsyncSenderMap& asmap() { return asmap_; } gu_thread_t thread() { return thread_; } private: friend class AsyncSenderMap; const gu::Config& conf_; std::string const peer_; wsrep_seqno_t const first_; wsrep_seqno_t const last_; wsrep_seqno_t const preload_start_; AsyncSenderMap& asmap_; gu_thread_t thread_; // GCC 4.8.5 on FreeBSD wants it AsyncSender(const AsyncSender&); AsyncSender& operator=(const AsyncSender&); }; } } std::string const galera::ist::Receiver::RECV_ADDR("ist.recv_addr"); std::string const galera::ist::Receiver::RECV_BIND("ist.recv_bind"); void galera::ist::register_params(gu::Config& conf) { conf.add(Receiver::RECV_ADDR, gu::Config::Flag::read_only); conf.add(Receiver::RECV_BIND, gu::Config::Flag::read_only); // Made hidden because undocumented conf.add(CONF_KEEP_KEYS, gu::Config::Flag::hidden | gu::Config::Flag::read_only | gu::Config::Flag::type_bool); } galera::ist::Receiver::Receiver(gu::Config& conf, gcache::GCache& gc, TrxHandleSlave::Pool& slave_pool, EventHandler& handler, const char* addr, gu::Progress::Callback* cb) : recv_addr_ (), recv_bind_ (), io_service_ (conf), acceptor_ (), mutex_ (), cond_ (), progress_cb_ (cb), first_seqno_ (WSREP_SEQNO_UNDEFINED), last_seqno_ (WSREP_SEQNO_UNDEFINED), current_seqno_(WSREP_SEQNO_UNDEFINED), conf_ (conf), gcache_ (gc), slave_pool_ (slave_pool), source_id_ (WSREP_UUID_UNDEFINED), handler_ (handler), thread_ (), error_code_ (0), version_ (-1), use_ssl_ (false), running_ (false), ready_ (false) { std::string recv_addr; std::string recv_bind; try { recv_bind = conf_.get(RECV_BIND); // no return } catch (gu::NotSet& e) {} try /* check if receive address is explicitly set */ { recv_addr = conf_.get(RECV_ADDR); return; } catch (gu::NotSet& e) {} /* if not, check the alternative. TODO: try to find from system. */ if (addr) { try { recv_addr = gu::URI(std::string("tcp://") + addr).get_host(); conf_.set(RECV_ADDR, recv_addr); } catch (gu::NotSet& e) {} } } galera::ist::Receiver::~Receiver() { } extern "C" void* run_receiver_thread(void* arg) { galera::ist::Receiver* receiver(static_cast(arg)); receiver->run(); return 0; } static void IST_fix_addr_scheme(const gu::Config& conf, std::string& addr) { /* check if explicit scheme is present */ if (addr.find("://") == std::string::npos) { #ifdef GALERA_HAVE_SSL try { std::string ssl_key = conf.get(gu::conf::ssl_key); bool dynamic_socket = false; if (conf.has(gu::conf::socket_dynamic)) { dynamic_socket = conf.get(gu::conf::socket_dynamic, false); } if (ssl_key.length() != 0 && not dynamic_socket) { addr.insert(0, "ssl://"); return; } } catch (gu::NotSet&) {} #endif // GALERA_HAVE_SSL addr.insert(0, "tcp://"); } } static void IST_fix_addr_port(const gu::Config& conf, const gu::URI& uri, std::string& addr) { try /* check for explicit port, TODO: make it possible to use any free port (explicit 0?) */ { uri.get_port(); } catch (gu::NotSet&) /* use gmcast listen port + 1 */ { int port(0); try { port = gu::from_string(conf.get(galera::BASE_PORT_KEY)); } catch (...) { port = gu::from_string(galera::BASE_PORT_DEFAULT); } port += 1; addr += ":" + gu::to_string(port); } } std::string galera::IST_determine_recv_addr (gu::Config& conf) { std::string recv_addr; try { recv_addr = conf.get(galera::ist::Receiver::RECV_ADDR); } catch (const gu::NotSet&) { try { recv_addr = conf.get(galera::BASE_HOST_KEY); } catch (const gu::NotSet&) { gu_throw_error(EINVAL) << "Could not determine IST receive address: '" << galera::ist::Receiver::RECV_ADDR << "' or '" << galera::BASE_HOST_KEY << "' not set."; } } IST_fix_addr_scheme(conf, recv_addr); gu::URI ra_uri(recv_addr); if (!conf.has(galera::BASE_HOST_KEY)) conf.set(galera::BASE_HOST_KEY, ra_uri.get_host()); IST_fix_addr_port(conf, ra_uri, recv_addr); log_info << "IST receiver addr using " << recv_addr; return recv_addr; } std::string galera::IST_determine_recv_bind(gu::Config& conf) { std::string recv_bind; recv_bind = conf.get(galera::ist::Receiver::RECV_BIND); IST_fix_addr_scheme(conf, recv_bind); gu::URI rb_uri(recv_bind); IST_fix_addr_port(conf, rb_uri, recv_bind); log_info << "IST receiver bind using " << recv_bind; return recv_bind; } std::string galera::ist::Receiver::prepare(wsrep_seqno_t const first_seqno, wsrep_seqno_t const last_seqno, int const version, const wsrep_uuid_t& source_id) { ready_ = false; version_ = version; source_id_ = source_id; recv_addr_ = IST_determine_recv_addr(conf_); try { recv_bind_ = IST_determine_recv_bind(conf_); } catch (gu::NotSet&) { recv_bind_ = recv_addr_; } // uri_bind will be the real bind address which the acceptor will // listen. The recv_addr_ returned from this call may point to // other address, for example if the node is behind NATting firewall. gu::URI const uri_bind(recv_bind_); try { if (uri_bind.get_scheme() == "ssl") { log_info << "IST receiver using ssl"; use_ssl_ = true; // Protocol versions prior 7 had a bug on sender side // which made sender to return null cert in handshake. // Therefore peer cert verfification must be enabled // only at protocol version 7 or higher. // Removed in 4.x asio refactoring. // gu::ssl_prepare_context(conf_, ssl_ctx_, version >= 7); } acceptor_ = io_service_.make_acceptor(uri_bind); acceptor_->listen(uri_bind); // read recv_addr_ from acceptor_ in case zero port was specified gu::URI const uri_addr(recv_addr_); recv_addr_ = uri_addr.get_scheme() + "://" + uri_addr.get_host() + ":" + gu::to_string(acceptor_->listen_port()); } catch (const gu::Exception& e) { recv_addr_ = ""; gu_throw_error(e.get_errno()) << "Failed to open IST listener at " << uri_bind.to_string() << "', asio error '" << e.what() << "'"; } first_seqno_ = first_seqno; last_seqno_ = last_seqno; int err; if ((err = gu_thread_create(&thread_, 0, &run_receiver_thread, this)) != 0) { recv_addr_ = ""; gu_throw_error(err) << "Unable to create receiver thread"; } running_ = true; log_info << "Prepared IST receiver for " << first_seqno << '-' << last_seqno << ", listening at: " << acceptor_->listen_addr(); return recv_addr_; } void galera::ist::Receiver::run() { auto socket(acceptor_->accept()); acceptor_->close(); /* shall be initialized below, when we know at what seqno preload starts */ gu::Progress* progress(NULL); int ec(0); std::ostringstream error_os; try { bool const keep_keys(conf_.get(CONF_KEEP_KEYS, CONF_KEEP_KEYS_DEFAULT)); Proto p(gcache_, version_, keep_keys); p.send_handshake(*socket); p.recv_handshake_response(*socket); p.send_ctrl(*socket, Ctrl::C_OK); // wait for SST to complete so that we know what is the first_seqno_ { gu::Lock lock(mutex_); while (ready_ == false) { lock.wait(cond_); } } log_info << "####### IST applying starts with " << first_seqno_; //remove assert(first_seqno_ > 0); bool preload_started(false); current_seqno_ = WSREP_SEQNO_UNDEFINED; while (true) { std::pair ret; p.recv_ordered(*socket, ret); gcs_action& act(ret.first); // act type GCS_ACT_UNKNOWN denotes EOF if (gu_unlikely(act.type == GCS_ACT_UNKNOWN)) { assert(0 == act.seqno_g); assert(NULL == act.buf); assert(0 == act.size); log_debug << "eof received, closing socket"; break; } assert(act.seqno_g > 0); if (gu_unlikely(WSREP_SEQNO_UNDEFINED == current_seqno_)) { assert(!progress); if (act.seqno_g > first_seqno_) { error_os << "IST started with wrong seqno: " << act.seqno_g << ", expected <= " << first_seqno_; ec = EINVAL; goto err; } log_info << "####### IST current seqno initialized to " << act.seqno_g; current_seqno_ = act.seqno_g; progress = new gu::Progress( progress_cb_, "Receiving IST", " events", last_seqno_ - current_seqno_ + 1, /* The following means reporting progress NO MORE frequently * than once per BOTH 10 seconds (default) and 16 events */ 16); } else { assert(progress); ++current_seqno_; progress->update(1); } if (act.seqno_g != current_seqno_) { error_os << "Unexpected action seqno: " << act.seqno_g << " expected: " << current_seqno_; ec = EINVAL; goto err; } assert(current_seqno_ > 0); assert(current_seqno_ == act.seqno_g); assert(act.type != GCS_ACT_UNKNOWN); bool const must_apply(current_seqno_ >= first_seqno_); bool const preload(ret.second); if (gu_unlikely(preload == true && preload_started == false)) { log_info << "IST preload starting at " << current_seqno_; preload_started = true; } switch (act.type) { case GCS_ACT_WRITESET: { TrxHandleSlavePtr ts( TrxHandleSlavePtr(TrxHandleSlave::New(false, slave_pool_), TrxHandleSlaveDeleter())); if (act.size > 0) { gu_trace(ts->unserialize(act)); ts->set_local(false); assert(ts->global_seqno() == act.seqno_g); assert(ts->depends_seqno() >= 0 || ts->nbo_end()); assert(ts->action().first && ts->action().second); // Checksum is verified later on } else { ts->set_global_seqno(act.seqno_g); ts->mark_dummy_with_action(act.buf); } //log_info << "####### Passing WS " << act.seqno_g; handler_.ist_trx(ts, must_apply, preload); break; } case GCS_ACT_CCHANGE: //log_info << "####### Passing IST CC " << act.seqno_g // << ", must_apply: " << must_apply // << ", preload: " << preload; handler_.ist_cc(act, must_apply, preload); break; default: assert(0); } } if (progress /* IST actually started */) progress->finish(); } catch (gu::Exception& e) { ec = e.get_errno(); if (ec != EINTR) { error_os << "got exception while reading IST stream: " << e.what(); } } err: delete progress; gu::Lock lock(mutex_); socket->close(); running_ = false; if (last_seqno_ > 0 && ec != EINTR && current_seqno_ < last_seqno_ && error_os.tellp() == 0) { error_os << "IST didn't contain all write sets, expected last: " << last_seqno_ << " last received: " << current_seqno_; ec = EPROTO; } if (ec != EINTR) { error_code_ = ec; } handler_.ist_end(Result{ec, error_os.str()}); } void galera::ist::Receiver::ready(wsrep_seqno_t const first) { assert(first > 0); gu::Lock lock(mutex_); first_seqno_ = first; ready_ = true; cond_.signal(); } wsrep_seqno_t galera::ist::Receiver::finished() { if (recv_addr_ == "") { log_debug << "IST was not prepared before calling finished()"; } else { interrupt(); int err; if ((err = gu_thread_join(thread_, 0)) != 0) { log_warn << "Failed to join IST receiver thread: " << err; } acceptor_->close(); gu::Lock lock(mutex_); running_ = false; recv_addr_ = ""; } return current_seqno_; } void galera::ist::Receiver::interrupt() { gu::URI uri(recv_addr_); try { auto socket(io_service_.make_socket(uri)); socket->connect(uri); Proto p(gcache_, version_, conf_.get(CONF_KEEP_KEYS, CONF_KEEP_KEYS_DEFAULT)); p.recv_handshake(*socket); p.send_ctrl(*socket, Ctrl::C_EOF); p.recv_ctrl(*socket); } catch (const gu::Exception&) { // ignore } } galera::ist::Sender::Sender(const gu::Config& conf, gcache::GCache& gcache, const std::string& peer, int version) : io_service_(conf), socket_ (), conf_ (conf), gcache_ (gcache), version_ (version), use_ssl_ (false) { gu::URI uri(peer); try { socket_ = io_service_.make_socket(uri); socket_->connect(uri); } catch (const gu::Exception& e) { gu_throw_error(e.get_errno()) << "IST sender, failed to connect '" << peer.c_str() << "': " << e.what(); } } galera::ist::Sender::~Sender() { socket_->close(); gcache_.seqno_unlock(); } void send_eof(galera::ist::Proto& p, gu::AsioSocket& socket) { p.send_ctrl(socket, galera::ist::Ctrl::C_EOF); // wait until receiver closes the connection try { gu::byte_t b; size_t n; n = socket.read(gu::AsioMutableBuffer(&b, 1)); if (n > 0) { log_warn << "received " << n << " bytes, expected none"; } } catch (const gu::Exception& e) { } } void galera::ist::Sender::send(wsrep_seqno_t first, wsrep_seqno_t last, wsrep_seqno_t preload_start) { if (first > last) { if (version_ < VER40) { assert(0); gu_throw_error(EINVAL) << "sender send first greater than last: " << first << " > " << last ; } } try { Proto p(gcache_, version_, conf_.get(CONF_KEEP_KEYS, CONF_KEEP_KEYS_DEFAULT)); int32_t ctrl; p.recv_handshake(*socket_); p.send_handshake_response(*socket_); ctrl = p.recv_ctrl(*socket_); if (ctrl < 0) { gu_throw_error(EPROTO) << "IST handshake failed, peer reported error: " << ctrl; } // send eof even if the set or transactions sent would be empty if (first > last || (first == 0 && last == 0)) { log_info << "IST sender notifying joiner, not sending anything"; send_eof(p, *socket_); return; } else { log_info << "IST sender " << first << " -> " << last; } std::vector buf_vec( std::min(static_cast(last - first + 1), static_cast(1024))); ssize_t n_read; while ((n_read = gcache_.seqno_get_buffers(buf_vec, first)) > 0) { GU_DBUG_SYNC_WAIT("ist_sender_send_after_get_buffers"); //log_info << "read " << first << " + " << n_read << " from gcache"; for (wsrep_seqno_t i(0); i < n_read; ++i) { // Preload start is the seqno of the lowest trx in // cert index at CC. If the cert index was completely // reset, preload_start will be zero and no preload flag // should be set. bool preload_flag(preload_start > 0 && buf_vec[i].seqno_g() >= preload_start); //log_info << "Sender::send(): seqno " << buf_vec[i].seqno_g() // << ", size " << buf_vec[i].size() << ", preload: " // << preload_flag; p.send_ordered(*socket_, buf_vec[i], preload_flag); if (buf_vec[i].seqno_g() == last) { send_eof(p, *socket_); return; } } first += n_read; // resize buf_vec to avoid scanning gcache past last size_t next_size(std::min(static_cast(last - first + 1), static_cast(1024))); if (buf_vec.size() != next_size) { buf_vec.resize(next_size); } } } catch (const gu::Exception& e) { gu_throw_error(e.get_errno()) << "ist send failed: " << "', asio error '" << e.what() << "'"; } } extern "C" void* run_async_sender(void* arg) { galera::ist::AsyncSender* as (reinterpret_cast(arg)); log_info << "async IST sender starting to serve " << as->peer().c_str() << " sending " << as->first() << "-" << as->last() << ", preload starts from " << as->preload_start(); wsrep_seqno_t join_seqno; try { as->send(as->first(), as->last(), as->preload_start()); join_seqno = as->last(); } catch (gu::Exception& e) { log_error << "async IST sender failed to serve " << as->peer().c_str() << ": " << e.what(); join_seqno = -e.get_errno(); } catch (...) { log_error << "async IST sender, failed to serve " << as->peer().c_str(); throw; } try { as->asmap().remove(as, join_seqno); gu_thread_detach(as->thread()); delete as; } catch (gu::NotFound& nf) { log_debug << "async IST sender already removed"; } log_info << "async IST sender served"; return 0; } void galera::ist::AsyncSenderMap::run(const gu::Config& conf, const std::string& peer, wsrep_seqno_t const first, wsrep_seqno_t const last, wsrep_seqno_t const preload_start, int const version) { gu::Critical crit(monitor_); AsyncSender* as(new AsyncSender(conf, peer, first, last, preload_start, *this, version)); int err(gu_thread_create(&as->thread_, 0, &run_async_sender, as)); if (err != 0) { delete as; gu_throw_system_error(err) << "failed to start sender thread"; } senders_.insert(as); } void galera::ist::AsyncSenderMap::remove(AsyncSender* as, wsrep_seqno_t seqno) { gu::Critical crit(monitor_); std::set::iterator i(senders_.find(as)); if (i == senders_.end()) { throw gu::NotFound(); } senders_.erase(i); } void galera::ist::AsyncSenderMap::cancel() { gu::Critical crit(monitor_); while (senders_.empty() == false) { AsyncSender* as(*senders_.begin()); senders_.erase(*senders_.begin()); int err; as->cancel(); monitor_.leave(); if ((err = gu_thread_join(as->thread_, 0)) != 0) { log_warn << "thread_join() failed: " << err; } monitor_.enter(); delete as; } } galera-4-26.4.22/galera/src/wsdb.hpp000644 000162 177776 00000007521 14755062442 020211 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2014 Codership Oy // #ifndef GALERA_WSDB_HPP #define GALERA_WSDB_HPP #include "trx_handle.hpp" #include "wsrep_api.h" #include "gu_unordered.hpp" namespace galera { class Wsdb { class Conn { public: Conn(wsrep_conn_id_t conn_id) : conn_id_(conn_id), trx_() { } Conn(const Conn& other) : conn_id_(other.conn_id_), trx_(other.trx_) { } ~Conn() { } void assign_trx(TrxHandleMasterPtr trx) { trx_ = trx; } void reset_trx() { trx_ = TrxHandleMasterPtr(); } TrxHandleMasterPtr get_trx() { return trx_; } private: void operator=(const Conn&); wsrep_conn_id_t conn_id_; TrxHandleMasterPtr trx_; }; class TrxHash { public: size_t operator()(const wsrep_trx_id_t& key) const { return key; } }; typedef gu::UnorderedMap TrxMap; class ConnHash { public: size_t operator()(const wsrep_conn_id_t& key) const { return key; } }; typedef gu::UnorderedMap ConnMap; public: TrxHandleMasterPtr get_trx(const TrxHandleMaster::Params& params, const wsrep_uuid_t& source_id, wsrep_trx_id_t trx_id, bool create =false); TrxHandleMasterPtr new_trx(const TrxHandleMaster::Params& params, const wsrep_uuid_t& source_id, wsrep_trx_id_t trx_id) { return TrxHandleMasterPtr(TrxHandleMaster::New(trx_pool_, params, source_id, -1, trx_id), TrxHandleMasterDeleter()); } void discard_trx(wsrep_trx_id_t trx_id); TrxHandleMasterPtr get_conn_query(const TrxHandleMaster::Params&, const wsrep_uuid_t&, wsrep_conn_id_t conn_id, bool create = false); void discard_conn_query(wsrep_conn_id_t conn_id); Wsdb(); ~Wsdb(); void print(std::ostream& os) const; struct stats { stats(size_t n_trx, size_t n_conn) : n_trx_(n_trx) , n_conn_(n_conn) { } size_t n_trx_; size_t n_conn_; }; stats get_stats() const { gu::Lock trx_lock(trx_mutex_); gu::Lock conn_lock(conn_mutex_); stats ret(trx_map_.size(), conn_map_.size()); return ret; } private: // Create new trx handle TrxHandleMasterPtr create_trx(const TrxHandleMaster::Params& params, const wsrep_uuid_t& source_id, wsrep_trx_id_t trx_id); Conn* get_conn(wsrep_conn_id_t conn_id, bool create); static const size_t trx_mem_limit_ = 1 << 20; TrxHandleMaster::Pool trx_pool_; TrxMap trx_map_; gu::Mutex trx_mutex_; ConnMap conn_map_; gu::Mutex conn_mutex_; }; inline std::ostream& operator<<(std::ostream& os, const Wsdb& w) { w.print(os); return os; } } #endif // GALERA_WSDB_HPP galera-4-26.4.22/galera/src/write_set.hpp000644 000162 177776 00000004160 14755062442 021253 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010 Codership Oy // #ifndef GALERA_WRITE_SET_HPP #define GALERA_WRITE_SET_HPP #include "key_os.hpp" #include "key_data.hpp" #include "wsrep_api.h" #include "gu_buffer.hpp" #include "gu_logger.hpp" #include "gu_unordered.hpp" #include #include #include namespace galera { class WriteSet { public: typedef std::deque KeySequence; WriteSet(int version) : version_(version), keys_(), key_refs_(), data_() { } void set_version(int version) { version_ = version; } const gu::Buffer& get_data() const { return data_; } void append_key(const KeyData&); void append_data(const void*data, size_t data_len) { data_.reserve(data_.size() + data_len); data_.insert(data_.end(), static_cast(data), static_cast(data) + data_len); } void get_keys(KeySequence&) const; const gu::Buffer& get_key_buf() const { return keys_; } bool empty() const { return (data_.size() == 0 && keys_.size() == 0); } void clear() { keys_.clear(), key_refs_.clear(), data_.clear(); } // Return offset to beginning of key or data segment and length // of that segment static std::pair segment(const gu::byte_t*, size_t, size_t); // Scan key sequence from buffer, return offset from the beginning of // buffer after scan. static size_t keys(const gu::byte_t*, size_t, size_t, int, KeySequence&); size_t serialize(gu::byte_t*, size_t, size_t) const; size_t unserialize(const gu::byte_t*, size_t, size_t); size_t serial_size() const; private: typedef gu::UnorderedMultimap KeyRefMap; int version_; gu::Buffer keys_; KeyRefMap key_refs_; gu::Buffer data_; }; } #endif // GALERA_WRITE_SET_HPP galera-4-26.4.22/galera/src/wsrep_provider.cpp000644 000162 177776 00000140011 14755062442 022307 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2021 Codership Oy // #include "key_data.hpp" #include "gu_serialize.hpp" #include "gu_asio.hpp" // // gu::init_allowlist_service_v1() #if defined(GALERA_MULTIMASTER) #include "replicator_smm.hpp" #define REPL_CLASS galera::ReplicatorSMM #else #error "Not implemented" #endif #include "wsrep_params.hpp" #include "gu_event_service.hpp" #include "wsrep_config_service.h" #include "wsrep_node_isolation.h" #include using galera::KeyOS; using galera::WriteSet; using galera::TrxHandle; using galera::TrxHandleMaster; using galera::TrxHandleSlave; using galera::TrxHandleLock; extern "C" { const char* wsrep_interface_version = (char*)WSREP_INTERFACE_VERSION; } extern "C" wsrep_status_t galera_init(wsrep_t* gh, const struct wsrep_init_args* args) { assert(gh != 0); try { gh->ctx = new REPL_CLASS (args); // Moved into galera::ReplicatorSMM::ParseOptions::ParseOptions() // wsrep_set_params(*reinterpret_cast(gh->ctx), // args->options); return WSREP_OK; } catch (gu::Exception& e) { log_error << e.what(); } #ifdef NDEBUG catch (std::exception& e) { log_error << e.what(); } catch (gu::NotFound& e) { /* Unrecognized parameter (logged by gu::Config::set()) */ } catch (...) { log_fatal << "non-standard exception"; } #endif return WSREP_NODE_FAIL; } extern "C" wsrep_cap_t galera_capabilities(wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); return repl->capabilities(); } extern "C" void galera_tear_down(wsrep_t* gh) { assert(gh != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); if (repl != 0) { delete repl; gh->ctx = 0; } } extern "C" wsrep_status_t galera_parameters_set (wsrep_t* gh, const char* params) { assert(gh != 0); // cppcheck-suppress nullPointer assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); // cppcheck-suppress nullPointer if (gh) { try { wsrep_set_params (*repl, params); return WSREP_OK; } catch (gu::NotFound&) { log_warn << "Unrecognized parameter in '" << params << "'"; return WSREP_WARNING; } catch (std::exception& e) { log_debug << e.what(); // better logged in wsrep_set_params } } else { log_error << "Attempt to set parameter(s) on uninitialized replicator."; } return WSREP_NODE_FAIL; } extern "C" char* galera_parameters_get (wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); try { REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); return wsrep_get_params(*repl); } catch (std::exception& e) { log_error << e.what(); return 0; } catch (...) { log_fatal << "non-standard exception"; return 0; } } extern "C" wsrep_status_t galera_enc_set_key(wsrep_t* gh, const wsrep_enc_key_t*key) { return WSREP_NOT_IMPLEMENTED; } extern "C" wsrep_status_t galera_connect (wsrep_t* gh, const char* cluster_name, const char* cluster_url, const char* state_donor, wsrep_bool_t bootstrap) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { return repl->connect(cluster_name, cluster_url, state_donor ? state_donor : "", bootstrap); } catch (gu::Exception& e) { log_error << "Failed to connect to cluster: " << e.what(); return WSREP_NODE_FAIL; } #ifdef NDEBUG catch (std::exception& e) { log_error << e.what(); return WSREP_NODE_FAIL; } catch (...) { log_fatal << "non-standard exception"; return WSREP_FATAL; } #endif /* NDEBUG */ } extern "C" wsrep_status_t galera_disconnect(wsrep_t *gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { return repl->close(); } catch (std::exception& e) { log_error << e.what(); return WSREP_NODE_FAIL; } catch (...) { log_fatal << "non-standard exception"; return WSREP_FATAL; } } extern "C" wsrep_status_t galera_recv(wsrep_t *gh, void *recv_ctx) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); #ifdef NDEBUG try { #endif /* NDEBUG */ return repl->async_recv(recv_ctx); #ifdef NDEBUG } catch (gu::Exception& e) { log_error << e.what(); switch (e.get_errno()) { case ENOTRECOVERABLE: return WSREP_FATAL; default: return WSREP_NODE_FAIL; } } catch (std::exception& e) { log_error << e.what(); } catch (...) { log_fatal << "non-standard exception"; } return WSREP_FATAL; #endif /* NDEBUG */ } static TrxHandleMaster* get_local_trx(REPL_CLASS* const repl, wsrep_ws_handle_t* const handle, bool const create) { TrxHandleMaster* trx(0); assert(handle != 0); if (handle->opaque != 0) { trx = static_cast(handle->opaque); assert(trx->trx_id() == handle->trx_id || wsrep_trx_id_t(-1) == handle->trx_id); } else { try { trx = repl->get_local_trx(handle->trx_id, create).get(); handle->opaque = trx; } catch (gu::NotFound& ) { } } return trx; } extern "C" wsrep_status_t galera_replay_trx(wsrep_t* gh, const wsrep_ws_handle_t* trx_handle, void* recv_ctx) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); TrxHandleMaster* trx(static_cast(trx_handle->opaque)); assert(trx != 0); assert(trx->ts() != 0); log_debug << "replaying " << *(trx->ts()); wsrep_status_t retval; try { TrxHandleLock lock(*trx); retval = repl->replay_trx(*trx, lock, recv_ctx); } catch (std::exception& e) { log_warn << "failed to replay trx: " << *trx; log_warn << e.what(); retval = WSREP_CONN_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } if (retval != WSREP_OK) { log_debug << "replaying failed for " << *(trx->ts()); } return retval; } extern "C" wsrep_status_t galera_abort_certification(wsrep_t* gh, wsrep_seqno_t bf_seqno, wsrep_trx_id_t victim_trx, wsrep_seqno_t* victim_seqno) { assert(gh != 0); assert(gh->ctx != 0); assert(victim_seqno != 0); *victim_seqno = WSREP_SEQNO_UNDEFINED; REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); wsrep_status_t retval; galera::TrxHandleMasterPtr txp(repl->get_local_trx(victim_trx)); if (!txp) { log_debug << "trx to abort " << victim_trx << " with bf seqno " << bf_seqno << " not found"; return WSREP_OK; } else { log_debug << "ABORTING trx " << victim_trx << " with bf seqno " << bf_seqno; } try { TrxHandleMaster& trx(*txp); TrxHandleLock lock(trx); retval = repl->abort_trx(trx, bf_seqno, victim_seqno); } catch (std::exception& e) { log_error << e.what(); retval = WSREP_NODE_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } GU_DBUG_SYNC_WAIT("abort_trx_end"); return retval; } extern "C" wsrep_status_t galera_rollback(wsrep_t* gh, wsrep_trx_id_t trx_id, const wsrep_buf_t* const data) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); galera::TrxHandleMasterPtr victim(repl->get_local_trx(trx_id)); /* Send the rollback fragment from a different context */ galera::TrxHandleMasterPtr trx(repl->new_local_trx(trx_id)); TrxHandleLock lock(*trx); if (data) { gu_trace(trx->append_data(data->ptr, data->len, WSREP_DATA_ORDERED, true)); } wsrep_trx_meta_t meta; meta.gtid = WSREP_GTID_UNDEFINED; meta.depends_on = WSREP_SEQNO_UNDEFINED; meta.stid.node = repl->source_id(); meta.stid.trx = trx_id; trx->set_flags(TrxHandle::EXPLICIT_ROLLBACK_FLAGS); trx->set_state(TrxHandle::S_MUST_ABORT); trx->set_state(TrxHandle::S_ABORTING); if (victim) { TrxHandleLock victim_lock(*victim); // Victim may already be in S_ABORTING state // if it was BF aborted in certify(). if (victim->state() != TrxHandle::S_ABORTING) { if (victim->state() != TrxHandle::S_MUST_ABORT) victim->set_state(TrxHandle::S_MUST_ABORT); victim->set_state(TrxHandle::S_ABORTING); } return repl->send(*trx, &meta); } return repl->send(*trx, &meta); } static inline void discard_local_trx(REPL_CLASS* repl, wsrep_ws_handle_t* ws_handle, TrxHandleMaster* trx) { repl->discard_local_trx(trx); ws_handle->opaque = 0; } static inline void append_data_array (TrxHandleMaster& trx, const struct wsrep_buf* const data, size_t const count, wsrep_data_type_t const type, bool const copy) { for (size_t i(0); i < count; ++i) { gu_trace(trx.append_data(data[i].ptr, data[i].len, type, copy)); } } extern "C" wsrep_status_t galera_assign_read_view(wsrep_t* const gh, wsrep_ws_handle_t* const handle, const wsrep_gtid_t* const rv) { return WSREP_NOT_IMPLEMENTED; } extern "C" wsrep_status_t galera_sync_wait(wsrep_t* const wsrep, wsrep_gtid_t* const upto, int tout, wsrep_gtid_t* const gtid) { assert(wsrep != 0); assert(wsrep->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(wsrep->ctx)); wsrep_status_t retval; try { retval = repl->sync_wait(upto, tout, gtid); } catch (std::exception& e) { log_warn << e.what(); retval = WSREP_CONN_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } return retval; } static wsrep_status_t galera_terminate_trx(wsrep_t* const gh, uint32_t const flags, wsrep_trx_meta_t* const meta) { assert((flags & WSREP_FLAG_PA_UNSAFE)); assert(!(flags & WSREP_FLAG_TRX_START)); assert((flags & WSREP_FLAG_TRX_END) || (flags & WSREP_FLAG_ROLLBACK)); REPL_CLASS* const repl(static_cast< REPL_CLASS * >(gh->ctx)); galera::TrxHandleMasterPtr trx(repl->new_trx(meta->stid.node, meta->stid.trx)); TrxHandleLock lock(*trx); trx->set_flags(TrxHandle::wsrep_flags_to_trx_flags(flags)); if ((flags & WSREP_FLAG_ROLLBACK)) { trx->set_state(TrxHandle::S_MUST_ABORT); trx->set_state(TrxHandle::S_ABORTING); } wsrep_status_t retval(repl->send(*trx, meta)); if (retval == WSREP_OK) { retval = galera_sync_wait(gh, NULL, -1, NULL); } return retval; } extern "C" wsrep_status_t wsrep_certify_v1(wsrep_t* gh, wsrep_conn_id_t conn_id, wsrep_ws_handle_t* trx_handle, uint32_t flags, wsrep_trx_meta_t* meta, const wsrep_seq_cb_t* seq_cb) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * const repl(static_cast< REPL_CLASS * >(gh->ctx)); TrxHandleMaster* txp(get_local_trx(repl, trx_handle, false)); // The following combinations of flags should not be set together assert(!((flags & WSREP_FLAG_TRX_START) && (flags & WSREP_FLAG_ROLLBACK))); assert(!((flags & WSREP_FLAG_TRX_PREPARE) && (flags & WSREP_FLAG_ROLLBACK))); assert(!((flags & WSREP_FLAG_TRX_PREPARE) && (flags & WSREP_FLAG_TRX_END))); if (gu_unlikely(txp == 0)) { if (meta != 0) { // If the caller passed a valid transaction id in meta, // then send a commit / rollback fragment to terminate // the transaction. // Notice that we are making two assumptions here: // 1) meta is treated as "in" parameter // 2) (uint64_t)-1 means "undefined transaction ID" // Rather than abusing galera_certify(), we should // expose this functionality through dedicated API, // and should be fixed next time we get a chance // to update the wsrep API (codership/wsrep-API#40). if (meta->stid.trx != (uint64_t)-1) { return galera_terminate_trx(gh, flags, meta); } else { meta->gtid = WSREP_GTID_UNDEFINED; meta->depends_on = WSREP_SEQNO_UNDEFINED; meta->stid.node = repl->source_id(); meta->stid.trx = -1; } } // no data to replicate return WSREP_OK; } TrxHandleMaster& trx(*txp); assert(trx.trx_id() != uint64_t(-1)); if (meta != 0) { meta->gtid = WSREP_GTID_UNDEFINED; meta->depends_on = WSREP_SEQNO_UNDEFINED; meta->stid.node = trx.source_id(); meta->stid.trx = trx.trx_id(); } wsrep_status_t retval; try { TrxHandleLock lock(trx); trx.set_conn_id(conn_id); trx.set_flags(trx.flags() | TrxHandle::wsrep_flags_to_trx_flags(flags)); if (flags & WSREP_FLAG_ROLLBACK) { if ((trx.flags() & (TrxHandle::F_BEGIN | TrxHandle::F_ROLLBACK)) == (TrxHandle::F_BEGIN | TrxHandle::F_ROLLBACK)) { return WSREP_TRX_MISSING; } trx.set_flags(trx.flags() | TrxHandle::F_PA_UNSAFE); if (trx.state() == TrxHandle::S_ABORTING) { trx.set_state(TrxHandle::S_EXECUTING); } } retval = repl->replicate(trx, meta, seq_cb); if (meta) { if (trx.ts()) { assert(meta->gtid.seqno > 0); assert(meta->gtid.seqno == trx.ts()->global_seqno()); // If TrxHandleSlave was queued its depends_seqno may be // modified concurrently. assert(trx.ts()->queued() || meta->depends_on == trx.ts()->depends_seqno()); } else { assert(meta->gtid.seqno == WSREP_SEQNO_UNDEFINED); assert(meta->depends_on == WSREP_SEQNO_UNDEFINED); } } assert(trx.trx_id() == meta->stid.trx); assert(!(retval == WSREP_OK || retval == WSREP_BF_ABORT) || (trx.ts() && trx.ts()->global_seqno() > 0)); if (retval == WSREP_OK) { assert(trx.state() != TrxHandle::S_MUST_ABORT); if ((flags & WSREP_FLAG_ROLLBACK) == 0) { assert(trx.ts() && trx.ts()->last_seen_seqno() >= 0); retval = repl->certify(trx, meta); assert(trx.state() != TrxHandle::S_MUST_ABORT || retval != WSREP_OK); if (meta) assert(meta->depends_on >= 0 || retval != WSREP_OK); } } else { if (meta) meta->depends_on = -1; } assert(retval == WSREP_OK || // success retval == WSREP_TRX_FAIL || // cert failure retval == WSREP_BF_ABORT || // BF abort retval == WSREP_CONN_FAIL|| // not in joined/synced state retval == WSREP_NODE_FAIL); // node inconsistent } catch (gu::Exception& e) { log_error << e.what(); if (e.get_errno() == EMSGSIZE) retval = WSREP_SIZE_EXCEEDED; else retval = WSREP_NODE_FAIL; } catch (std::exception& e) { log_error << e.what(); retval = WSREP_NODE_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } trx.release_write_set_out(); return retval; } extern "C" wsrep_status_t galera_certify(wsrep_t* const gh, wsrep_conn_id_t const conn_id, wsrep_ws_handle_t* const trx_handle, uint32_t const flags, wsrep_trx_meta_t* const meta) { return wsrep_certify_v1(gh, conn_id, trx_handle, flags, meta, nullptr); } extern "C" wsrep_status_t galera_commit_order_enter( wsrep_t* const gh, const wsrep_ws_handle_t* const ws_handle, const wsrep_trx_meta_t* const meta ) { assert(gh != 0); assert(gh->ctx != 0); assert(ws_handle != 0); REPL_CLASS * const repl(static_cast< REPL_CLASS * >(gh->ctx)); TrxHandle* const txp(static_cast(ws_handle->opaque)); assert(NULL != txp); if (txp == 0) { log_warn << "Trx " << ws_handle->trx_id << " not found for commit order enter"; return WSREP_TRX_MISSING; } wsrep_status_t retval; try { if (txp->master()) { TrxHandleMaster& trx(*reinterpret_cast(txp)); TrxHandleLock lock(trx); // assert(trx.state() != TrxHandle::S_REPLAYING); if (gu_unlikely(trx.state() == TrxHandle::S_MUST_ABORT)) { if (trx.ts() && (trx.ts()->flags() & TrxHandle::F_COMMIT)) { trx.set_state(TrxHandle::S_MUST_REPLAY); return WSREP_BF_ABORT; } else { trx.set_state(TrxHandle::S_ABORTING); return WSREP_TRX_FAIL; } } retval = repl->commit_order_enter_local(trx); } else { TrxHandleSlave& ts(*reinterpret_cast(txp)); retval = repl->commit_order_enter_remote(ts); } } catch (std::exception& e) { log_error << e.what(); retval = WSREP_NODE_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } return retval; } extern "C" wsrep_status_t galera_commit_order_leave( wsrep_t* const gh, const wsrep_ws_handle_t* const ws_handle, const wsrep_trx_meta_t* const meta, const wsrep_buf_t* const error ) { assert(gh != 0); assert(gh->ctx != 0); assert(ws_handle != 0); REPL_CLASS * const repl(static_cast< REPL_CLASS * >(gh->ctx)); TrxHandle* const txp(static_cast(ws_handle->opaque)); assert(NULL != txp); if (txp == NULL) { log_warn << "Trx " << ws_handle->trx_id << " not found for commit order leave"; return WSREP_TRX_MISSING; } wsrep_status_t retval; try { if (txp->master()) { TrxHandleMaster& trx(*reinterpret_cast(txp)); TrxHandleLock lock(trx); assert(trx.ts() && trx.ts()->global_seqno() > 0); if (trx.state() == TrxHandle::S_MUST_ABORT) { // Trx is non-committing streaming replication and // the trx was BF aborted while committing a fragment. // At this point however, we can't know if the // fragment is already committed into DBMS fragment storage // or not, so we return a success. The BF abort error // is returned to the caller from galera_release(). assert(!(trx.ts()->flags() & TrxHandle::F_COMMIT)); trx.set_state(TrxHandle::S_ABORTING); retval = repl->commit_order_leave(*trx.ts(), error); trx.set_deferred_abort(true); } else { retval = repl->commit_order_leave(*trx.ts(), error); assert(trx.state() == TrxHandle::S_ROLLING_BACK || trx.state() == TrxHandle::S_COMMITTING || !(trx.ts()->flags() & TrxHandle::F_COMMIT)); trx.set_state(trx.state() == TrxHandle::S_ROLLING_BACK ? TrxHandle::S_ROLLED_BACK : TrxHandle::S_COMMITTED); } } else { TrxHandleSlave& ts(*reinterpret_cast(txp)); retval = repl->commit_order_leave(ts, error); } } catch (std::exception& e) { log_error << "commit_order_leave(): " << e.what(); retval = WSREP_NODE_FAIL; } catch (...) { log_fatal << "commit_order_leave(): non-standard exception"; retval = WSREP_FATAL; } return retval; } extern "C" wsrep_status_t galera_release(wsrep_t* gh, wsrep_ws_handle_t* ws_handle) { assert(gh != 0); assert(gh->ctx != 0); // A trx object was not created for this handle if (not ws_handle->opaque) return WSREP_OK; REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); TrxHandleMaster* txp(get_local_trx(repl, ws_handle, false)); if (txp == 0) { log_debug << "trx " << ws_handle->trx_id << " not found for release"; return WSREP_OK; } wsrep_status_t retval; bool discard_trx(true); try { TrxHandleMaster& trx(*txp); TrxHandleLock lock(trx); if (trx.state() == TrxHandle::S_MUST_ABORT) { // This is possible in case of ALG due to a race: BF applier BF // aborts trx that has already grabbed commit monitor and is // committing. This is possible only if aborter is ordered after // the victim, and since for regular committing transactions such // abort is unnecessary, this should be possible only for ongoing // streaming transactions. galera::TrxHandleSlavePtr ts(trx.ts()); if (ts && ts->flags() & TrxHandle::F_COMMIT) { log_warn << "trx was BF aborted during commit: " << *ts; assert(0); // manipulate state to avoid crash trx.set_state(TrxHandle::S_MUST_REPLAY); trx.set_state(TrxHandle::S_REPLAYING); } else { // Streaming replication, not in commit phase. Must abort. log_debug << "SR trx was BF aborted during commit: " << trx; trx.set_state(TrxHandle::S_ABORTING); } } if (gu_likely(trx.state() == TrxHandle::S_COMMITTED)) { assert(!trx.deferred_abort()); retval = repl->release_commit(trx); assert(trx.state() == TrxHandle::S_COMMITTED || trx.state() == TrxHandle::S_EXECUTING); if (trx.state() == TrxHandle::S_EXECUTING && retval == WSREP_OK) { // SR trx ready for new fragment, keep transaction discard_trx = false; } } else if (trx.deferred_abort() == false) { retval = repl->release_rollback(trx); assert(trx.state() == TrxHandle::S_ROLLED_BACK); } else if (trx.state() == TrxHandle::S_ABORTING) { assert(trx.deferred_abort()); // SR trx was BF aborted before commit_order_leave() // We return BF abort error code here and do not clean up // the transaction. The transaction is needed for sending // rollback fragment. retval = WSREP_BF_ABORT; discard_trx = false; trx.set_deferred_abort(false); } else { assert(0); gu_throw_fatal << "Internal program error: " "unexpected state in deferred abort trx: " << trx; } switch(trx.state()) { case TrxHandle::S_COMMITTED: case TrxHandle::S_ROLLED_BACK: case TrxHandle::S_EXECUTING: case TrxHandle::S_ABORTING: break; default: assert(0); gu_throw_fatal << "Internal library error: " "unexpected trx release state: " << trx; } } catch (std::exception& e) { log_error << e.what(); assert(0); retval = WSREP_NODE_FAIL; } catch (...) { log_fatal << "non-standard exception"; assert(0); retval = WSREP_FATAL; } if (discard_trx) { discard_local_trx(repl, ws_handle, txp); } return retval; } extern "C" wsrep_status_t galera_append_key(wsrep_t* const gh, wsrep_ws_handle_t* const trx_handle, const wsrep_key_t* const keys, size_t const keys_num, wsrep_key_type_t const key_type, wsrep_bool_t const copy) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); TrxHandleMaster* trx(get_local_trx(repl, trx_handle, true)); assert(trx != 0); wsrep_status_t retval; try { int const proto_ver(repl->trx_proto_ver()); TrxHandleLock lock(*trx); if (keys_num > 0) { for (size_t i(0); i < keys_num; ++i) { galera::KeyData const k(proto_ver, keys[i].key_parts, keys[i].key_parts_num, key_type, copy); gu_trace(trx->append_key(k)); } } else if (proto_ver >= 6) { /* Append server-level key (matches every trx)*/ galera::KeyData const k(proto_ver, key_type); gu_trace(trx->append_key(k)); } retval = WSREP_OK; } catch (gu::Exception& e) { log_warn << e.what(); if (EMSGSIZE == e.get_errno()) retval = WSREP_SIZE_EXCEEDED; else retval = WSREP_CONN_FAIL; //? } catch (std::exception& e) { log_warn << e.what(); retval = WSREP_CONN_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } return retval; } extern "C" wsrep_status_t galera_append_data(wsrep_t* const wsrep, wsrep_ws_handle_t* const trx_handle, const struct wsrep_buf* const data, size_t const count, wsrep_data_type_t const type, wsrep_bool_t const copy) { assert(wsrep != 0); assert(wsrep->ctx != 0); assert(data != NULL); assert(count > 0); if (data == NULL) { // no data to replicate return WSREP_OK; } REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(wsrep->ctx)); TrxHandleMaster* txp(get_local_trx(repl, trx_handle, true)); assert(txp != 0); TrxHandleMaster& trx(*txp); wsrep_status_t retval; try { TrxHandleLock lock(trx); gu_trace(append_data_array(trx, data, count, type, copy)); retval = WSREP_OK; } catch (gu::Exception& e) { log_warn << e.what(); if (EMSGSIZE == e.get_errno()) retval = WSREP_SIZE_EXCEEDED; else retval = WSREP_CONN_FAIL; //? } catch (std::exception& e) { log_warn << e.what(); retval = WSREP_CONN_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } return retval; } extern "C" wsrep_status_t galera_last_committed_id(wsrep_t* const wsrep, wsrep_gtid_t* const gtid) { assert(wsrep != 0); assert(wsrep->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(wsrep->ctx)); wsrep_status_t retval; try { retval = repl->last_committed_id(gtid); } catch (std::exception& e) { log_warn << e.what(); retval = WSREP_CONN_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } return retval; } extern "C" wsrep_status_t galera_free_connection(wsrep_t* const gh, wsrep_conn_id_t const conn_id) { assert(gh != 0); assert(gh->ctx != 0); // This function is now no-op and can be removed from the // future versions. Connection object is allocated only from // galera_to_execute_start() and will be released either // from that function in case of failure or from // galera_to_execute_end(). return WSREP_OK; } extern "C" wsrep_status_t galera_to_execute_start(wsrep_t* const gh, wsrep_conn_id_t const conn_id, const wsrep_key_t* const keys, size_t const keys_num, const struct wsrep_buf* const data, size_t const count, uint32_t const flags, wsrep_trx_meta_t* const meta) { assert(gh != 0); assert(gh->ctx != 0); // Non-blocking operations "certification" depends on // TRX_START and TRX_END flags, not having those flags may cause // undefined behavior so check them here. assert(flags & (WSREP_FLAG_TRX_START | WSREP_FLAG_TRX_END)); if ((flags & (WSREP_FLAG_TRX_START | WSREP_FLAG_TRX_END)) == 0) { log_warn << "to_execute_start(): either WSREP_FLAG_TRX_START " << "or WSREP_FLAG_TRX_END flag is required"; return WSREP_CONN_FAIL; } // Simultaneous use of TRX_END AND ROLLBACK is not allowed assert(!((flags & WSREP_FLAG_TRX_END) && (flags & WSREP_FLAG_ROLLBACK))); if ((flags & WSREP_FLAG_TRX_END) && (flags & WSREP_FLAG_ROLLBACK)) { log_warn << "to_execute_start(): simultaneous use of " << "WSREP_FLAG_TRX_END and WSREP_FLAG_ROLLBACK " << "is not allowed"; return WSREP_CONN_FAIL; } REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); galera::TrxHandleMasterPtr txp(repl->local_conn_trx(conn_id, true)); assert(txp != 0); TrxHandleMaster& trx(*txp.get()); assert(trx.state() == TrxHandle::S_EXECUTING); trx.set_flags(TrxHandle::wsrep_flags_to_trx_flags( flags | WSREP_FLAG_ISOLATION)); // NBO-end event. Application should have provided the ongoing // operation start event source node id and connection id in // meta->stid.node and meta->stid.conn respectively if (trx.nbo_end() == true) { galera::NBOKey key(meta->gtid.seqno); gu::Buffer buf(galera::NBOKey::serial_size()); (void)key.serialize(&buf[0], buf.size(), 0); struct wsrep_buf data_buf = {&buf[0], buf.size()}; gu_trace(append_data_array(trx, &data_buf, 1, WSREP_DATA_ORDERED,true)); } if (meta != 0) { // Don't override trx meta gtid for NBO end yet, gtid is used in // replicator wait_nbo_end() to locate correct nbo context if (trx.nbo_end() == false) { meta->gtid = WSREP_GTID_UNDEFINED; } meta->depends_on = WSREP_SEQNO_UNDEFINED; meta->stid.node = trx.source_id(); meta->stid.trx = trx.trx_id(); meta->stid.conn = trx.conn_id(); } wsrep_status_t retval; try { TrxHandleLock lock(trx); for (size_t i(0); i < keys_num; ++i) { galera::KeyData k(repl->trx_proto_ver(), keys[i].key_parts, keys[i].key_parts_num, WSREP_KEY_EXCLUSIVE,false); gu_trace(trx.append_key(k)); } gu_trace(append_data_array(trx, data, count, WSREP_DATA_ORDERED, false)); if (trx.nbo_end() == false) { retval = repl->replicate(trx, meta, nullptr); assert((retval == WSREP_OK && trx.ts() != 0 && trx.ts()->global_seqno() > 0) || (retval != WSREP_OK && (trx.ts() == 0 || trx.ts()->global_seqno() < 0))); if (meta) { if (trx.ts()) { assert(meta->gtid.seqno > 0); assert(meta->gtid.seqno == trx.ts()->global_seqno()); assert(meta->depends_on == trx.ts()->depends_seqno()); } else { assert(meta->gtid.seqno == WSREP_SEQNO_UNDEFINED); assert(meta->depends_on == WSREP_SEQNO_UNDEFINED); } } } else { // NBO-end events are broadcasted separately in to_isolation_begin() retval = WSREP_OK; } if (retval == WSREP_OK) { retval = repl->to_isolation_begin(trx, meta); } } catch (gu::Exception& e) { log_error << e.what(); if (e.get_errno() == EMSGSIZE) retval = WSREP_SIZE_EXCEEDED; else retval = WSREP_CONN_FAIL; } catch (std::exception& e) { log_warn << e.what(); retval = WSREP_CONN_FAIL; } catch (...) { log_fatal << "non-standard exception"; retval = WSREP_FATAL; } if (trx.ts() == NULL || trx.ts()->global_seqno() < 0) { // galera_to_execute_end() won't be called repl->discard_local_conn_trx(conn_id); // trx is not needed anymore meta->gtid = WSREP_GTID_UNDEFINED; } return retval; } extern "C" wsrep_status_t galera_to_execute_end(wsrep_t* const gh, wsrep_conn_id_t const conn_id, const wsrep_buf_t* const err) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); galera::TrxHandleMasterPtr trx(repl->local_conn_trx(conn_id, false)); assert(trx != 0); if (trx == 0) { log_warn << "No trx handle for connection " << conn_id << " in galera_to_execute_end()"; return WSREP_CONN_FAIL; } wsrep_status_t ret(WSREP_OK); try { TrxHandleLock lock(*trx); repl->to_isolation_end(*trx, err); } catch (std::exception& e) { log_error << "to_execute_end(): " << e.what(); ret = WSREP_NODE_FAIL; } catch (...) { log_fatal << "to_execute_end(): non-standard exception"; ret = WSREP_FATAL; } gu_trace(repl->discard_local_conn_trx(conn_id)); // trx will be unreferenced (destructed) during purge repl->discard_local_conn_trx(conn_id); return ret; } extern "C" wsrep_status_t galera_preordered_collect (wsrep_t* const gh, wsrep_po_handle_t* const handle, const struct wsrep_buf* const data, size_t const count, wsrep_bool_t const copy) { assert(gh != 0); assert(gh->ctx != 0); assert(handle != 0); assert(data != 0); assert(count > 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { return repl->preordered_collect(*handle, data, count, copy); } catch (std::exception& e) { log_warn << e.what(); return WSREP_TRX_FAIL; } catch (...) { log_fatal << "non-standard exception"; return WSREP_FATAL; } } extern "C" wsrep_status_t galera_preordered_commit (wsrep_t* const gh, wsrep_po_handle_t* const handle, const wsrep_uuid_t* const source_id, uint32_t const flags, int const pa_range, wsrep_bool_t const commit) { assert(gh != 0); assert(gh->ctx != 0); assert(handle != 0); assert(source_id != 0 || false == commit); assert(pa_range >= 0 || false == commit); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { return repl->preordered_commit(*handle, *source_id, flags, pa_range, commit); } catch (std::exception& e) { log_warn << e.what(); return WSREP_TRX_FAIL; } catch (...) { log_fatal << "non-standard exception"; return WSREP_FATAL; } } extern "C" wsrep_status_t galera_sst_sent (wsrep_t* const gh, const wsrep_gtid_t* const state_id, int const rcode) { assert(gh != 0); assert(gh->ctx != 0); assert(state_id != 0); assert(rcode <= 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); return repl->sst_sent(*state_id, rcode); } extern "C" wsrep_status_t galera_sst_received (wsrep_t* const gh, const wsrep_gtid_t* const state_id, const wsrep_buf_t* const state, int const rcode) { assert(gh != 0); assert(gh->ctx != 0); assert(state_id != 0); assert(rcode <= 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); if (rcode < 0) { assert(state_id->seqno == WSREP_SEQNO_UNDEFINED); } return repl->sst_received(*state_id, state, rcode); } extern "C" wsrep_status_t galera_snapshot(wsrep_t* const wsrep, const wsrep_buf_t* const msg, const char* const donor_spec) { return WSREP_NOT_IMPLEMENTED; } extern "C" struct wsrep_stats_var* galera_stats_get (wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS* repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); return const_cast(repl->stats_get()); } extern "C" void galera_stats_free (wsrep_t* gh, struct wsrep_stats_var* s) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS* repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); return repl->stats_free(s); //REPL_CLASS::stats_free(s); } extern "C" void galera_stats_reset (wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS* repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); repl->stats_reset(); } extern "C" wsrep_seqno_t galera_pause (wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { return repl->pause(); } catch (gu::Exception& e) { log_warn << "Node pause failed: " << e.what(); return -e.get_errno(); } } extern "C" wsrep_status_t galera_resume (wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { repl->resume(); return WSREP_OK; } catch (gu::Exception& e) { log_error << "Node resume failed: " << e.what(); return WSREP_NODE_FAIL; } } extern "C" wsrep_status_t galera_desync (wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { repl->desync(); return WSREP_OK; } catch (gu::Exception& e) { log_warn << "Node desync failed: " << e.what(); return WSREP_TRX_FAIL; } } extern "C" wsrep_status_t galera_resync (wsrep_t* gh) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); try { repl->resync(); return WSREP_OK; } catch (gu::Exception& e) { log_error << "Node resync failed: " << e.what(); return WSREP_NODE_FAIL; } } extern "C" wsrep_status_t galera_lock (wsrep_t* gh, const char* name, wsrep_bool_t shared, uint64_t owner, int64_t timeout) { assert(gh != 0); assert(gh->ctx != 0); return WSREP_NOT_IMPLEMENTED; } extern "C" wsrep_status_t galera_unlock (wsrep_t* gh, const char* name, uint64_t owner) { assert(gh != 0); assert(gh->ctx != 0); return WSREP_OK; } extern "C" bool galera_is_locked (wsrep_t* gh, const char* name, uint64_t* owner, wsrep_uuid_t* node) { assert(gh != 0); assert(gh->ctx != 0); return false; } static wsrep_t galera_str = { WSREP_INTERFACE_VERSION, &galera_init, &galera_capabilities, &galera_parameters_set, &galera_parameters_get, &galera_enc_set_key, &galera_connect, &galera_disconnect, &galera_recv, &galera_assign_read_view, &galera_certify, &galera_commit_order_enter, &galera_commit_order_leave, &galera_release, &galera_replay_trx, &galera_abort_certification, &galera_rollback, &galera_append_key, &galera_append_data, &galera_sync_wait, &galera_last_committed_id, &galera_free_connection, &galera_to_execute_start, &galera_to_execute_end, &galera_preordered_collect, &galera_preordered_commit, &galera_sst_sent, &galera_sst_received, &galera_snapshot, &galera_stats_get, &galera_stats_free, &galera_stats_reset, &galera_pause, &galera_resume, &galera_desync, &galera_resync, &galera_lock, &galera_unlock, &galera_is_locked, "Galera", GALERA_VER "(r" GALERA_REV ")", "Codership Oy ", &galera_tear_down, NULL, NULL }; /* Prototype to make compiler happy */ extern "C" int wsrep_loader(wsrep_t *hptr); extern "C" int wsrep_loader(wsrep_t *hptr) { if (!hptr) return EINVAL; try { *hptr = galera_str; } catch (...) { return ENOTRECOVERABLE; } return WSREP_OK; } extern "C" int wsrep_init_allowlist_service_v1(wsrep_allowlist_service_v1_t *allowlist_service) { return gu::init_allowlist_service_v1(allowlist_service); } extern "C" void wsrep_deinit_allowlist_service_v1() { gu::deinit_allowlist_service_v1(); } extern "C" int wsrep_init_event_service_v1(wsrep_event_service_v1_t *event_service) { return gu::EventService::init_v1(event_service); } extern "C" void wsrep_deinit_event_service_v1() { gu::EventService::deinit_v1(); } static int map_parameter_flags(int flags) { int ret = 0; if (flags & gu::Config::Flag::deprecated) ret |= WSREP_PARAM_DEPRECATED; if (flags & gu::Config::Flag::read_only) ret |= WSREP_PARAM_READONLY; if (flags & gu::Config::Flag::type_bool) ret |= WSREP_PARAM_TYPE_BOOL; if (flags & gu::Config::Flag::type_integer) ret |= WSREP_PARAM_TYPE_INTEGER; if (flags & gu::Config::Flag::type_double) ret |= WSREP_PARAM_TYPE_DOUBLE; if (flags & gu::Config::Flag::type_duration) ret |= WSREP_PARAM_TYPE_DOUBLE; return ret; } static int wsrep_parameter_init(wsrep_parameter& wsrep_param, const std::string& key, const gu::Config::Parameter& param) { wsrep_param.flags = map_parameter_flags(param.flags()); wsrep_param.name = key.c_str(); const char* ret = ""; switch (param.flags() & gu::Config::Flag::type_mask) { case gu::Config::Flag::type_bool: ret = gu_str2bool(param.value().c_str(), &wsrep_param.value.as_bool); break; case gu::Config::Flag::type_integer: { long long tmp; ret = gu_str2ll(param.value().c_str(), &tmp); wsrep_param.value.as_integer = tmp; break; } case gu::Config::Flag::type_double: ret = gu_str2dbl(param.value().c_str(), &wsrep_param.value.as_double); break; case gu::Config::Flag::type_duration: { try { // durations are mapped to doubles wsrep_param.value.as_double = to_double(gu::datetime::Period(param.value())); } catch (...) { assert(0); return 1; } break; } default: assert((param.flags() & gu::Config::Flag::type_mask) == 0); wsrep_param.value.as_string = param.value().c_str(); } if (*ret != '\0') { return 1; } return 0; } static wsrep_status_t get_parameters(wsrep_t* gh, wsrep_get_parameters_cb callback, void* context) { assert(gh != 0); assert(gh->ctx != 0); REPL_CLASS * repl(reinterpret_cast< REPL_CLASS * >(gh->ctx)); const gu::Config& config(repl->params()); for (auto &i : config) { const std::string& key(i.first); const gu::Config::Parameter& param(i.second); if (!param.is_hidden()) { wsrep_parameter arg; if (wsrep_parameter_init(arg, key, param) || (callback(&arg, context) != WSREP_OK)) { log_error << "Failed to initialize parameter '" << key << "', value " << param.value() << " , flags (" << gu::Config::Flag::to_string(param.flags()) << ")"; return WSREP_FATAL; } } } return WSREP_OK; } extern "C" int wsrep_init_config_service_v1(wsrep_config_service_v1_t *config_service) { config_service->get_parameters = get_parameters; // Deprecation checks will be done by application which uses // the service. gu::Config::disable_deprecation_check(); return WSREP_OK; } extern "C" void wsrep_deinit_config_service_v1() { gu::Config::enable_deprecation_check(); } /* * This function may be called from signal handler, so make sure that * only 'safe' system calls and library functions are used. See * https://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html */ extern "C" enum wsrep_node_isolation_result wsrep_node_isolation_mode_set_v1(enum wsrep_node_isolation_mode mode) { if (mode < WSREP_NODE_ISOLATION_NOT_ISOLATED || mode > WSREP_NODE_ISOLATION_FORCE_DISCONNECT) { return WSREP_NODE_ISOLATION_INVALID_VALUE; } gu::gu_asio_node_isolation_mode = mode; return WSREP_NODE_ISOLATION_SUCCESS; } galera-4-26.4.22/galera/src/replicator.cpp000644 000162 177776 00000001317 14755062442 021406 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2014 Codership Oy // #include "replicator.hpp" namespace galera { std::string const Replicator::Param::debug_log = "debug"; #ifdef GU_DBUG_ON std::string const Replicator::Param::dbug = "dbug"; std::string const Replicator::Param::signal = "signal"; #endif /* GU_DBUG_ON */ void Replicator::register_params(gu::Config& conf) { conf.add(Param::debug_log, "no", gu::Config::Flag::type_bool); #ifdef GU_DBUG_ON conf.add(Param::dbug, ""); conf.add(Param::signal, ""); #endif /* GU_DBUG_ON */ } const char* const Replicator::TRIVIAL_SST(WSREP_STATE_TRANSFER_TRIVIAL); const char* const Replicator::NO_SST(WSREP_STATE_TRANSFER_NONE); } /* namespace galera */ galera-4-26.4.22/galera/src/trx_handle.cpp000644 000162 177776 00000036466 14755062442 021407 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2018 Codership Oy // #include "trx_handle.hpp" #include "galera_exception.hpp" #include #include const galera::TrxHandleMaster::Params galera::TrxHandleMaster::Defaults(".", -1, KeySet::MAX_VERSION, gu::RecordSet::VER2, false); void galera::TrxHandle::print_state(std::ostream& os, TrxHandle::State s) { switch (s) { case TrxHandle::S_EXECUTING: os << "EXECUTING"; return; case TrxHandle::S_MUST_ABORT: os << "MUST_ABORT"; return; case TrxHandle::S_ABORTING: os << "ABORTING"; return; case TrxHandle::S_REPLICATING: os << "REPLICATING"; return; case TrxHandle::S_CERTIFYING: os << "CERTIFYING"; return; case TrxHandle::S_MUST_REPLAY: os << "MUST_REPLAY"; return; case TrxHandle::S_REPLAYING: os << "REPLAYING"; return; case TrxHandle::S_APPLYING: os << "APPLYING"; return; case TrxHandle::S_COMMITTING: os << "COMMITTING"; return; case TrxHandle::S_ROLLING_BACK: os << "ROLLING_BACK"; return; case TrxHandle::S_COMMITTED: os << "COMMITTED"; return; case TrxHandle::S_ROLLED_BACK: os << "ROLLED_BACK"; return; // don't use default to make compiler warn if something is missed } os << "(s) << ">"; assert(0); } std::ostream& galera::operator<<(std::ostream& os, TrxHandle::State const s) { galera::TrxHandle::print_state(os, s); return os; } void galera::TrxHandle::print_set_state(State state) const { log_info << "Trx: " << this << " shifting to " << state; } void galera::TrxHandle::print_state_history(std::ostream& os) const { const std::vector& hist(state_.history()); for (size_t i(0); i < hist.size(); ++i) { os << hist[i].first << ':' << hist[i].second << "->"; } const TrxHandle::Fsm::StateEntry current_state(state_.get_state_entry()); os << current_state.first << ':' << current_state.second; } inline void galera::TrxHandle::print(std::ostream& os) const { os << "source: " << source_id() << " version: " << version() << " local: " << local() << " flags: " << flags() << " conn_id: " << int64_t(conn_id()) << " trx_id: " << int64_t(trx_id()) // for readability << " tstamp: " << timestamp() << "; state: "; } void galera::TrxHandleMaster::print(std::ostream& os) const { TrxHandle::print(os); print_state_history(os); } std::ostream& galera::operator<<(std::ostream& os, const TrxHandleMaster& th) { th.print(os); return os; } void galera::TrxHandleSlave::print(std::ostream& os) const { TrxHandle::print(os); os << " seqnos (l: " << local_seqno_ << ", g: " << global_seqno_ << ", s: " << last_seen_seqno_ << ", d: " << depends_seqno_ << ")"; if (!skip_event()) { os << " WS pa_range: " << write_set().pa_range(); if (write_set().annotated()) { os << "\nAnnotation:\n"; write_set().write_annotation(os); os << std::endl; } } else { os << " skip event"; } os << "; state history: "; print_state_history(os); } std::ostream& galera::operator<<(std::ostream& os, const TrxHandleSlave& th) { th.print(os); return os; } galera::TrxHandleMaster::Fsm::TransMap galera::TrxHandleMaster::trans_map_; galera::TrxHandleSlave::Fsm::TransMap galera::TrxHandleSlave::trans_map_; namespace galera { // // About transaction states: // // The TrxHandleMaster stats are used to track the state of the // transaction, while TrxHandleSlave states are used to track // which critical sections have been accessed during write set // applying. As a convention, TrxHandleMaster states are changed // before entering the critical section, TrxHandleSlave states // after critical section has been successfully entered. // // TrxHandleMaster states during normal execution: // // EXECUTING - Transaction handle has been created by appending key // or write set data // REPLICATING - Transaction write set has been send to group // communication layer for ordering // CERTIFYING - Transaction write set has been received from group // communication layer, has entered local monitor and // is certifying // APPLYING - Transaction has entered applying critical section // COMMITTING - Transaction has entered committing critical section // COMMITTED - Transaction has released commit time critical section // ROLLED_BACK - Application performed a voluntary rollback // // Note that streaming replication rollback happens by replicating // special rollback writeset which will go through regular write set // critical sections. // // Note/Fixme: CERTIFYING, APPLYING and COMMITTING states seem to be // redundant as these states can be tracked via // associated TrxHandleSlave states. // // // TrxHandleMaster states after effective BF abort: // // MUST_ABORT - Transaction enter this state after successful BF abort. // BF abort is allowed if: // * Transaction does not have associated TrxHandleSlave // * Transaction has associated TrxHandleSlave but it does // not have commit flag set // * Transaction has associated TrxHandleSlave, commit flag // is set and the TrxHandleSlave global sequence number is // higher than BF aborter global sequence number // // 1) If the certification after BF abort results a failure: // ABORTING - BF abort was effective and certification // resulted a failure // ROLLING_BACK - Commit order critical section has been grabbed for // rollback // ROLLED_BACK - Commit order critical section has been released after // successful rollback // // 2) The case where BF abort happens after successful certification or // if out-of-order certification results a success: // MUST_REPLAY - The transaction must roll back and replay in applier // context. // * If the BF abort happened before certification, // certification must be performed in applier context // and the transaction replay must be aborted if // the certification fails. // * TrxHandleSlave state can be used to determine // which critical sections must be entered before the // replay. For example, if the TrxHandleSlave state is // REPLICATING, write set must be certified under local // monitor and both apply and commit monitors must be // entered before applying. On the other hand, if // TrxHandleSlave state is APPLYING, only commit monitor // must be grabbed before replay. // // TrxHandleMaster states after replication failure: // // ABORTING - Replicaition resulted a failure // ROLLING_BACK - Error has been returned to application // ROLLED_BACK - Application has finished rollback // // // TrxHandleMaster states after certification failure: // // ABORTING - Certification resulted a failure // ROLLING_BACK - Commit order critical section has been grabbed for // rollback // ROLLED_BACK - Commit order critical section has been released // after successful rollback // // // // TrxHandleSlave: // REPLICATING - this is the first state for TrxHandleSlave after it // has been received from group // CERTIFYING - local monitor has been entered successfully // APPLYING - apply monitor has been entered successfully // COMMITTING - commit monitor has been entered successfully // // TrxHandleSlave state machine is restricted in order to use it // for tracking which monitors have been entered. Certification result // can be queried via is_dummy(). // // State machine diagrams can be found below. template<> TransMapBuilder::TransMapBuilder() : trans_map_(TrxHandleMaster::trans_map_) { // // 0 COMMITTED <-| // | ^ | // | SR | | // | |------------------------------------------------------| | // v v | | // EXECUTING -> REPLICATING -> CERTIFYING -> APPLYING -> COMMITTING | // |^ | | | | | | // || |------------------------------------------------------- | // || | BF Abort ----------------| | // || v | Cert Fail | // ||MUST_ABORT ----------------------------------------- | // || | | | | // || Pre Repl | v | REPLAYING // || | MUST_CERT_AND_REPLAY --------------| ^ // || SR Rollback v | ----------| Cert OK | // | --------- ABORTING <------- | v | // | | Cert Fail | MUST_REPLAY_AM | // | v | | | // | ROLLING_BACK | v | // | | |-> MUST_REPLAY_CM | // | v | | | // ----------> ROLLED_BACK | v | // |-> MUST_REPLAY | // | | // ------------ // // Executing add(TrxHandle::S_EXECUTING, TrxHandle::S_REPLICATING); add(TrxHandle::S_EXECUTING, TrxHandle::S_ROLLED_BACK); add(TrxHandle::S_EXECUTING, TrxHandle::S_MUST_ABORT); // Replicating add(TrxHandle::S_REPLICATING, TrxHandle::S_CERTIFYING); add(TrxHandle::S_REPLICATING, TrxHandle::S_MUST_ABORT); // Certifying add(TrxHandle::S_CERTIFYING, TrxHandle::S_APPLYING); add(TrxHandle::S_CERTIFYING, TrxHandle::S_ABORTING); add(TrxHandle::S_CERTIFYING, TrxHandle::S_MUST_ABORT); // Applying add(TrxHandle::S_APPLYING, TrxHandle::S_COMMITTING); add(TrxHandle::S_APPLYING, TrxHandle::S_MUST_ABORT); // Committing add(TrxHandle::S_COMMITTING, TrxHandle::S_COMMITTED); add(TrxHandle::S_COMMITTING, TrxHandle::S_MUST_ABORT); add(TrxHandle::S_COMMITTED, TrxHandle::S_EXECUTING); // SR // BF aborted add(TrxHandle::S_MUST_ABORT, TrxHandle::S_MUST_REPLAY); add(TrxHandle::S_MUST_ABORT, TrxHandle::S_ABORTING); // Replay, BF abort happens on application side after // commit monitor has been grabbed add(TrxHandle::S_MUST_REPLAY, TrxHandle::S_REPLAYING); // In-order certification failed for BF'ed action add(TrxHandle::S_MUST_REPLAY, TrxHandle::S_ABORTING); // Replay stage add(TrxHandle::S_REPLAYING, TrxHandle::S_COMMITTING); // BF aborted add(TrxHandle::S_ABORTING, TrxHandle::S_ROLLED_BACK); // cert failed or BF in apply monitor add(TrxHandle::S_ABORTING, TrxHandle::S_ROLLING_BACK); add(TrxHandle::S_ROLLING_BACK, TrxHandle::S_ROLLED_BACK); // SR rollback add(TrxHandle::S_ABORTING, TrxHandle::S_EXECUTING); } template<> TransMapBuilder::TransMapBuilder() : trans_map_(TrxHandleSlave::trans_map_) { // Cert OK // 0 --> REPLICATING -> CERTIFYING -> APPLYING -> COMMITTING -> COMMITTED // // Enter in-order cert after replication add(TrxHandle::S_REPLICATING, TrxHandle::S_CERTIFYING); // Applying after certification add(TrxHandle::S_CERTIFYING, TrxHandle::S_APPLYING); // Entering commit monitor after applying add(TrxHandle::S_APPLYING, TrxHandle::S_COMMITTING); // Commit finished add(TrxHandle::S_COMMITTING, TrxHandle::S_COMMITTED); } static TransMapBuilder master; static TransMapBuilder slave; } /* namespace galera */ void galera::TrxHandleSlave::sanity_checks() const { if (gu_unlikely((flags() & (F_ROLLBACK | F_BEGIN)) == (F_ROLLBACK | F_BEGIN))) { log_warn << "Both F_BEGIN and F_ROLLBACK are set on trx. " << "This trx should not have been replicated at all: " << *this; assert(0); } } void galera::TrxHandleSlave::deserialize_error_log(const gu::Exception& e) const { log_fatal << "Writeset deserialization failed: " << e.what() << std::endl << "WS flags: " << write_set_flags_ << std::endl << "Trx proto: " << version_ << std::endl << "Trx source: " << source_id_ << std::endl << "Trx conn_id: " << conn_id_ << std::endl << "Trx trx_id: " << trx_id_ << std::endl << "Trx last_seen: " << last_seen_seqno_; } void galera::TrxHandleSlave::apply (void* recv_ctx, wsrep_apply_cb_t apply_cb, const wsrep_trx_meta_t& meta, wsrep_bool_t& exit_loop) { uint32_t const wsrep_flags(trx_flags_to_wsrep_flags(flags())); int err(0); const DataSetIn& ws(write_set_.dataset()); void* err_msg(NULL); size_t err_len(0); ws.rewind(); // make sure we always start from the beginning wsrep_ws_handle_t const wh = { trx_id(), this }; if (ws.count() > 0) { for (ssize_t i = 0; WSREP_CB_SUCCESS == err && i < ws.count(); ++i) { const gu::Buf& buf(ws.next()); wsrep_buf_t const wb = { buf.ptr, size_t(buf.size) }; err = apply_cb(recv_ctx, &wh, wsrep_flags, &wb, &meta, &exit_loop); } } else { // Apply also zero sized write set to inform application side // about transaction meta data. wsrep_buf_t const wb = { NULL, 0 }; err = apply_cb(recv_ctx, &wh, wsrep_flags, &wb, &meta, &exit_loop); assert(NULL == err_msg); assert(0 == err_len); } if (gu_unlikely(err != WSREP_CB_SUCCESS)) { std::ostringstream os; os << "Apply callback failed: Trx: " << *this << ", status: " << err; galera::ApplyException ae(os.str(), err_msg, NULL, err_len); GU_TRACE(ae); throw ae; } return; } /* we don't care about any failures in applying unordered events */ void galera::TrxHandleSlave::unordered(void* recv_ctx, wsrep_unordered_cb_t cb) const { if (NULL != cb && write_set_.unrdset().count() > 0) { const DataSetIn& unrd(write_set_.unrdset()); for (int i(0); i < unrd.count(); ++i) { const gu::Buf& data(unrd.next()); wsrep_buf_t const wb = { data.ptr, size_t(data.size) }; cb(recv_ctx, &wb); } } } void galera::TrxHandleSlave::destroy_local(void* ptr) { assert(ptr); (static_cast(ptr))->~TrxHandleMaster(); } galera-4-26.4.22/galera/src/certification.hpp000644 000162 177776 00000022405 14755062442 022073 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2018 Codership Oy // #ifndef GALERA_CERTIFICATION_HPP #define GALERA_CERTIFICATION_HPP #include "nbo.hpp" #include "trx_handle.hpp" #include "key_entry_ng.hpp" #include "galera_service_thd.hpp" #include "galera_view.hpp" #include #include #include #include #include #include #include namespace galera { class Certification { public: static std::string const PARAM_LOG_CONFLICTS; static std::string const PARAM_OPTIMISTIC_PA; static void register_params(gu::Config&); typedef gu::UnorderedSet CertIndex; typedef gu::UnorderedSet CertIndexNG; typedef gu::UnorderedMultiset CertIndexNBO; private: typedef std::multiset DepsSet; typedef std::map TrxMap; public: typedef enum { TEST_OK, TEST_FAILED } TestResult; Certification(gu::Config& conf, ServiceThd* thd); ~Certification(); void assign_initial_position(const gu::GTID& gtid, int version); TestResult append_trx(const TrxHandleSlavePtr&); /* Append dummy trx from cert index preload. */ void append_dummy_preload(const TrxHandleSlavePtr&); wsrep_seqno_t position() const { return position_; } /* this is for configuration change use */ void adjust_position(const View&, const gu::GTID& gtid, int version); wsrep_seqno_t get_safe_to_discard_seqno() const { gu::Lock lock(mutex_); return get_safe_to_discard_seqno_(); } wsrep_seqno_t purge_trxs_upto(wsrep_seqno_t const seqno, bool const handle_gcache) { gu::Lock lock(mutex_); const wsrep_seqno_t stds(get_safe_to_discard_seqno_()); // assert(seqno <= get_safe_to_discard_seqno()); // Note: setting trx committed is not done in total order so // safe to discard seqno may decrease. Enable assertion above when // this issue is fixed. return purge_trxs_upto_(std::min(seqno, stds), handle_gcache); } // Set trx corresponding to handle committed. Return purge seqno if // index purge is required, -1 otherwise. wsrep_seqno_t set_trx_committed(TrxHandleSlave&); // statistics section void stats_get(double& avg_cert_interval, double& avg_deps_dist, size_t& index_size) const { gu::Lock lock(stats_mutex_); avg_cert_interval = 0; avg_deps_dist = 0; if (n_certified_) { avg_cert_interval = double(cert_interval_) / n_certified_; avg_deps_dist = double(deps_dist_) / n_certified_; } index_size = index_size_; } void stats_reset() { gu::Lock lock(stats_mutex_); cert_interval_ = 0; deps_dist_ = 0; n_certified_ = 0; index_size_ = 0; } void param_set(const std::string& key, const std::string& value); wsrep_seqno_t lowest_trx_seqno() const { return (trx_map_.empty() ? position_ : trx_map_.begin()->first); } // // NBO context lifecycle: // * Context is created when NBO-start event is received // * Context stays in nbo_ctx_map_ until client calls erase_nbo_ctx() // // Get NBO context matching to global seqno gu::shared_ptr::type nbo_ctx(wsrep_seqno_t); // Erase NBO context entry void erase_nbo_ctx(wsrep_seqno_t); size_t nbo_size() const { return nbo_map_.size(); } void mark_inconsistent(); bool is_inconsistent() const { return inconsistent_; } private: // Non-copyable Certification(const Certification&); Certification& operator=(const Certification&); TestResult test(const TrxHandleSlavePtr&); TestResult do_test(const TrxHandleSlavePtr&); TestResult do_test_v3to6(TrxHandleSlave*); TestResult do_test_preordered(TrxHandleSlave*); TestResult do_test_nbo(const TrxHandleSlavePtr&); void purge_for_trx(TrxHandleSlave*); // unprotected variants for internal use wsrep_seqno_t get_safe_to_discard_seqno_() const; wsrep_seqno_t purge_trxs_upto_(wsrep_seqno_t, bool sync); gu::shared_ptr::type nbo_ctx_unlocked(wsrep_seqno_t); bool index_purge_required() { static unsigned int const KEYS_THRESHOLD (1 << 10); // 1K static unsigned int const BYTES_THRESHOLD(128 << 20); // 128M static unsigned int const TRXS_THRESHOLD (127); /* if either key count, byte count or trx count exceed their * threshold, zero up counts and return true. */ return ((key_count_ > KEYS_THRESHOLD || byte_count_ > BYTES_THRESHOLD || trx_count_ > TRXS_THRESHOLD) && (key_count_ = 0, byte_count_ = 0, trx_count_ = 0, true)); } class PurgeAndDiscard { public: PurgeAndDiscard(Certification& cert) : cert_(cert) { } void operator()(TrxMap::value_type& vt) const { if (not vt.second) { // Dummy preload events insert only seqno return; } { TrxHandleSlave* trx(vt.second.get()); // Trying to lock trx mutex here may cause deadlock // with streaming replication. Locking can be skipped // because trx is only read here and refcount uses atomics. // Memory barrier is provided by certification mutex. // // TrxHandleLock lock(*trx); if (!cert_.is_inconsistent()) { assert(trx->is_committed() == true); if (trx->is_committed() == false) { log_warn <<"trx not committed in purge and discard: " << *trx; } } // If depends seqno is not WSREP_SEQNO_UNDEFINED // write set certification has passed and keys have been // inserted into index and purge is needed. // TOI write sets will always pass regular certification // and keys will be inserted, however if they fail // NBO certification depends seqno is set to // WSREP_SEQNO_UNDEFINED. Therefore purge should always // be done for TOI write sets. if (trx->is_dummy() == false || trx->is_toi() == true) { cert_.purge_for_trx(trx); } } } PurgeAndDiscard(const PurgeAndDiscard& other) : cert_(other.cert_) { } private: void operator=(const PurgeAndDiscard&); Certification& cert_; }; int version_; gu::Config& conf_; TrxMap trx_map_; CertIndexNG cert_index_ng_; NBOMap nbo_map_; NBOCtxMap nbo_ctx_map_; CertIndexNBO nbo_index_; TrxHandleSlave::Pool nbo_pool_; DepsSet deps_set_; View current_view_; ServiceThd* service_thd_; gu::Mutex mutex_; size_t trx_size_warn_count_; wsrep_seqno_t initial_position_; wsrep_seqno_t position_; wsrep_seqno_t nbo_position_; wsrep_seqno_t safe_to_discard_seqno_; wsrep_seqno_t last_pa_unsafe_; wsrep_seqno_t last_preordered_seqno_; wsrep_trx_id_t last_preordered_id_; gu::Mutex stats_mutex_; size_t n_certified_; wsrep_seqno_t deps_dist_; wsrep_seqno_t cert_interval_; size_t index_size_; size_t key_count_; size_t byte_count_; size_t trx_count_; /* The only reason those are not static constants is because * there might be a need to thange them without recompilation. * see #454 */ int const max_length_; /* Purge trx_map_ when it exceeds this * NOTE: this effectively sets a limit * on trx certification interval */ unsigned int const max_length_check_; /* Mask how often to check */ bool inconsistent_; bool log_conflicts_; bool optimistic_pa_; }; } #endif // GALERA_CERTIFICATION_HPP galera-4-26.4.22/galera/src/wsdb.cpp000644 000162 177776 00000007477 14755062442 020216 0ustar00jenkinsnogroup000000 000000 /* * Copyright (C) 2010-2014 Codership Oy */ #include "wsdb.hpp" #include "trx_handle.hpp" #include "write_set.hpp" #include "gu_lock.hpp" #include "gu_throw.hpp" #include void galera::Wsdb::print(std::ostream& os) const { os << "trx map:\n"; for (galera::Wsdb::TrxMap::const_iterator i = trx_map_.begin(); i != trx_map_.end(); ++i) { os << i->first << " " << *i->second << "\n"; } os << "conn query map:\n"; for (galera::Wsdb::ConnMap::const_iterator i = conn_map_.begin(); i != conn_map_.end(); ++i) { os << i->first << " "; } os << "\n"; } galera::Wsdb::Wsdb() : trx_pool_ (TrxHandleMaster::LOCAL_STORAGE_SIZE(), 512, "LocalTrxHandle"), trx_map_ (), trx_mutex_ (), conn_map_ (), conn_mutex_() {} galera::Wsdb::~Wsdb() { log_info << "wsdb trx map usage " << trx_map_.size() << " conn query map usage " << conn_map_.size(); log_info << trx_pool_; #ifndef NDEBUG log_info << *this; assert(trx_map_.size() == 0); assert(conn_map_.size() == 0); #endif // !NDEBUG } inline galera::TrxHandleMasterPtr galera::Wsdb::create_trx(const TrxHandleMaster::Params& params, const wsrep_uuid_t& source_id, wsrep_trx_id_t const trx_id) { TrxHandleMasterPtr trx(new_trx(params, source_id, trx_id)); std::pair i (trx_map_.insert(std::make_pair(trx_id, trx))); if (gu_unlikely(i.second == false)) gu_throw_fatal; return i.first->second; } galera::TrxHandleMasterPtr galera::Wsdb::get_trx(const TrxHandleMaster::Params& params, const wsrep_uuid_t& source_id, wsrep_trx_id_t const trx_id, bool const create) { gu::Lock lock(trx_mutex_); TrxMap::iterator const i(trx_map_.find(trx_id)); if (i == trx_map_.end() && create) { return create_trx(params, source_id, trx_id); } else if (i == trx_map_.end()) { return TrxHandleMasterPtr(); } return i->second; } galera::Wsdb::Conn* galera::Wsdb::get_conn(wsrep_conn_id_t const conn_id, bool const create) { gu::Lock lock(conn_mutex_); ConnMap::iterator i(conn_map_.find(conn_id)); if (conn_map_.end() == i) { if (create == true) { std::pair p (conn_map_.insert(std::make_pair(conn_id, Conn(conn_id)))); if (gu_unlikely(p.second == false)) gu_throw_fatal; return &p.first->second; } return 0; } return &(i->second); } galera::TrxHandleMasterPtr galera::Wsdb::get_conn_query(const TrxHandleMaster::Params& params, const wsrep_uuid_t& source_id, wsrep_conn_id_t const conn_id, bool const create) { Conn* const conn(get_conn(conn_id, create)); if (0 == conn) { throw gu::NotFound(); } if (conn->get_trx() == 0 && create == true) { TrxHandleMasterPtr trx (TrxHandleMaster::New(trx_pool_, params, source_id, conn_id, -1), TrxHandleMasterDeleter()); conn->assign_trx(trx); } return conn->get_trx(); } void galera::Wsdb::discard_trx(wsrep_trx_id_t trx_id) { gu::Lock lock(trx_mutex_); TrxMap::iterator i; if ((i = trx_map_.find(trx_id)) != trx_map_.end()) { trx_map_.erase(i); } } void galera::Wsdb::discard_conn_query(wsrep_conn_id_t conn_id) { gu::Lock lock(conn_mutex_); ConnMap::iterator i; if ((i = conn_map_.find(conn_id)) != conn_map_.end()) { i->second.reset_trx(); conn_map_.erase(i); } } galera-4-26.4.22/galera/src/nbo.hpp000644 000162 177776 00000011024 14755062442 020021 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2015 Codership Oy // #ifndef GALERA_NBO_HPP #define GALERA_NBO_HPP #include "galera_view.hpp" #include "gu_buffer.hpp" #include "gu_serialize.hpp" #include "gu_logger.hpp" #include "gu_lock.hpp" #include "trx_handle.hpp" #include "wsrep_api.h" #include namespace galera { class TrxHandleSlave; class MappedBuffer; // Helper datatypes for NBO // Context to be shared between cert NBOEntry and TrxHandleSlave // to signal ending of NBO. class NBOCtx { public: NBOCtx() : mutex_(), cond_ (), ts_ (), aborted_(false) { } void set_ts(const TrxHandleSlavePtr& ts) { gu::Lock lock(mutex_); assert(ts != 0); assert(ts->global_seqno() != WSREP_SEQNO_UNDEFINED); ts_ = ts; cond_.broadcast(); } wsrep_seqno_t seqno() const { gu::Lock lock(mutex_); return (ts_ == 0 ? WSREP_SEQNO_UNDEFINED : ts_->global_seqno()); } TrxHandleSlavePtr wait_ts() { gu::Lock lock(mutex_); while (ts_ == 0) { try { lock.wait(cond_, gu::datetime::Date::calendar() + gu::datetime::Sec); } catch (const gu::Exception& e) { if (e.get_errno() == ETIMEDOUT) { return TrxHandleSlavePtr(); } throw; } } return ts_; } void set_aborted(bool val) { gu::Lock lock(mutex_); aborted_= val; cond_.broadcast(); } bool aborted() const { gu::Lock lock(mutex_); return aborted_; } private: NBOCtx(const NBOCtx&); NBOCtx& operator=(const NBOCtx&); gu::Mutex mutex_; gu::Cond cond_; TrxHandleSlavePtr ts_; bool aborted_; }; // Key for NBOMap class NBOKey { public: NBOKey() : seqno_(WSREP_SEQNO_UNDEFINED) { } NBOKey(const wsrep_seqno_t seqno) : seqno_(seqno) { } wsrep_seqno_t seqno() const { return seqno_; } bool operator<(const NBOKey& other) const { return (seqno_ < other.seqno_); } size_t serialize(gu::byte_t* buf, size_t buf_len, size_t offset) { return gu::serialize8(seqno_, buf, buf_len, offset); } size_t unserialize(const gu::byte_t* buf, size_t buf_len, size_t offset) { return gu::unserialize8(buf, buf_len, offset, seqno_); } static size_t serial_size() { return 8; //gu::serial_size8(wsrep_seqno_t()); } private: wsrep_seqno_t seqno_; }; // Entry for NBOMap class NBOEntry { public: NBOEntry( gu::shared_ptr::type ts, gu::shared_ptr::type buf, gu::shared_ptr::type nbo_ctx) : ts_ (ts), buf_(buf), ended_set_(), nbo_ctx_(nbo_ctx) { } TrxHandleSlave* ts_ptr() { return ts_.get(); } // const TrxHandleSlave* ts_ptr() const { return ts_.get(); } void add_ended(const wsrep_uuid_t& uuid) { std::pair ret( ended_set_.insert(uuid)); if (ret.second == false) { log_warn << "duplicate entry " << uuid << " for ended set"; } } void clear_ended() { ended_set_.clear(); } const View::MembSet& ended_set() const { return ended_set_; } void end(const TrxHandleSlavePtr& ts) { assert(ts != 0); nbo_ctx_->set_ts(ts); } gu::shared_ptr::type nbo_ctx() { return nbo_ctx_; } private: gu::shared_ptr::type ts_; gu::shared_ptr::type buf_; View::MembSet ended_set_; gu::shared_ptr::type nbo_ctx_; }; typedef std::map::type> NBOCtxMap; typedef std::map NBOMap; } #endif // !GALERA_NBO_HPP galera-4-26.4.22/galera/src/gcs_action_source.cpp000644 000162 177776 00000013447 14755062442 022742 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2020 Codership Oy // #include "replicator.hpp" #include "gcs_action_source.hpp" #include "trx_handle.hpp" #include "gu_serialize.hpp" #include "gu_throw.hpp" #include "galera_info.hpp" #include // Exception-safe way to release action pointer when it goes out // of scope class Release { public: Release(struct gcs_action& act, gcache::GCache& gcache) : act_(act), gcache_(gcache) {} ~Release() { switch (act_.type) { case GCS_ACT_WRITESET: case GCS_ACT_CCHANGE: // these are ordered and should be released when no longer needed break; case GCS_ACT_STATE_REQ: gcache_.free(const_cast(act_.buf)); break; default: ::free(const_cast(act_.buf)); break; } } private: struct gcs_action& act_; gcache::GCache& gcache_; }; void galera::GcsActionSource::process_writeset(void* const recv_ctx, const struct gcs_action& act, bool& exit_loop) { assert(act.seqno_g > 0); assert(act.seqno_l != GCS_SEQNO_ILL); TrxHandleSlavePtr tsp(TrxHandleSlave::New(false, trx_pool_), TrxHandleSlaveDeleter()); gu_trace(tsp->unserialize(act)); tsp->set_local(replicator_.source_id() == tsp->source_id()); gu_trace(replicator_.process_trx(recv_ctx, tsp)); exit_loop = tsp->exit_loop(); // this is the end of trx lifespan } void galera::GcsActionSource::resend_writeset(const struct gcs_action& act) { assert(act.seqno_g == -EAGAIN); assert(act.seqno_l == GCS_SEQNO_ILL); ssize_t ret; struct gu_buf const sb = { act.buf, act.size }; GcsI::WriteSetVector v; v.resize(1); v[0] = sb; /* grab send monitor to resend asap */ while ((ret = gcs_.sendv(v, act.size, act.type, false, true)) == -EAGAIN) { usleep(1000); } if (ret > 0) { log_debug << "Local action " << gcs_act_type_to_str(act.type) << " of size " << ret << '/' << act.size << " was resent."; /* release source buffer */ gcache_.free(const_cast(act.buf)); } else { gu_throw_fatal << "Failed to resend action {" << act.buf << ", " << act.size << ", " << gcs_act_type_to_str(act.type) << "}"; } } void galera::GcsActionSource::dispatch(void* const recv_ctx, const struct gcs_action& act, bool& exit_loop) { assert(act.buf != 0); assert(act.seqno_l > 0 || act.seqno_g == -EAGAIN); switch (act.type) { case GCS_ACT_WRITESET: if (act.seqno_g > 0) { process_writeset(recv_ctx, act, exit_loop); } else { resend_writeset(act); } break; case GCS_ACT_COMMIT_CUT: { wsrep_seqno_t seqno; gu::unserialize8(act.buf, act.size, 0, seqno); assert(seqno >= 0); gu_trace(replicator_.process_commit_cut(seqno, act.seqno_l)); break; } case GCS_ACT_CCHANGE: gu_trace(replicator_.process_conf_change(recv_ctx, act)); break; case GCS_ACT_STATE_REQ: gu_trace(replicator_.process_state_req(recv_ctx, act.buf, act.size, act.seqno_l, act.seqno_g)); break; case GCS_ACT_JOIN: { wsrep_seqno_t seq; gu::unserialize8(static_cast(act.buf), act.size, 0, seq); gu_trace(replicator_.process_join(seq, act.seqno_l)); break; } case GCS_ACT_SYNC: gu_trace(replicator_.process_sync(act.seqno_l)); break; case GCS_ACT_VOTE: { int64_t seqno; size_t const off(gu::unserialize8(act.buf, act.size, 0, seqno)); int64_t code; gu::unserialize8(act.buf, act.size, off, code); assert(seqno >= 0); gu_trace(replicator_.process_vote(seqno, act.seqno_l, code)); break; } default: gu_throw_fatal << "unrecognized action type: " << act.type; } } ssize_t galera::GcsActionSource::process(void* recv_ctx, bool& exit_loop) { struct gcs_action act; ssize_t rc(gcs_.recv(act)); /* Potentially we want to do corrupt() check inside commit_monitor_ as well * but by the time inconsistency is detected an arbitrary number of * transactions may be already committed, so no reason to try that hard * in a critical section */ bool const skip(replicator_.corrupt() && GCS_ACT_CCHANGE != act.type && GCS_ACT_VOTE != act.type && /* action needs resending */ -EAGAIN != act.seqno_g); if (gu_likely(rc > 0 && !skip)) { Release release(act, gcache_); if (-EAGAIN != act.seqno_g /* replicated action */) { ++received_; received_bytes_ += rc; } try { gu_trace(dispatch(recv_ctx, act, exit_loop)); } catch (gu::Exception& e) { log_error << "Failed to process action " << act << ": " << e.what(); rc = -e.get_errno(); } } else if (rc > 0 && skip) { Release release(act, gcache_); replicator_.cancel_seqnos(act.seqno_l, act.seqno_g); } else { assert(act.seqno_l < 0); assert(act.seqno_g < 0); if (GCS_ACT_INCONSISTENCY == act.type) { assert(0 == rc); rc = INCONSISTENCY_CODE; } } return rc; } galera-4-26.4.22/galera/src/ist_proto.hpp000644 000162 177776 00000065743 14755062442 021306 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2011-2019 Codership Oy // #ifndef GALERA_IST_PROTO_HPP #define GALERA_IST_PROTO_HPP #include "gcs.hpp" #include "trx_handle.hpp" #include "GCache.hpp" #include "gu_asio.hpp" #include "gu_logger.hpp" #include "gu_serialize.hpp" #include "gu_vector.hpp" #include "gu_array.hpp" #include // // Message class must have non-virtual destructor until // support up to version 3 is removed as serialization/deserialization // depends on the size of the class. // #include "gu_disable_non_virtual_dtor.hpp" // // Sender Receiver // connect() -----> accept() // <----- send_handshake() // send_handshake_response() -----> // <----- send_ctrl(OK) // send_trx() -----> // -----> // send_ctrl(EOF) -----> // <----- close() // close() // // Note about protocol/message versioning: // Version is determined by GCS and IST protocol is initialized in total // order. Therefore it is not necessary to negotiate version at IST level, // it should be enough to check that message version numbers match. // namespace galera { namespace ist { static int const VER21 = 4; static int const VER40 = 10; class Message { public: typedef enum { T_NONE = 0, T_HANDSHAKE = 1, T_HANDSHAKE_RESPONSE = 2, T_CTRL = 3, T_TRX = 4, T_CCHANGE = 5, T_SKIP = 6 } Type; typedef enum { F_PRELOAD = 0x1 } Flag; explicit Message(int version, Type type = T_NONE, uint8_t flags = 0, int8_t ctrl = 0, uint32_t len = 0, wsrep_seqno_t seqno = WSREP_SEQNO_UNDEFINED) : seqno_ (seqno ), len_ (len ), type_ (type ), version_(version), flags_ (flags ), ctrl_ (ctrl ) {} int version() const { return version_; } Type type() const { return type_ ; } uint8_t flags() const { return flags_ ; } int8_t ctrl() const { return ctrl_ ; } uint32_t len() const { return len_ ; } wsrep_seqno_t seqno() const { return seqno_ ; } void set_type_seqno(Type t, wsrep_seqno_t s) { type_ = t; seqno_ = s; } ~Message() { } size_t serial_size() const { if (gu_likely(version_ >= VER40)) { // header: version 1 byte, type 1 byte, flags 1 byte, // ctrl field 1 byte, length 4 bytes, seqno 8 bytes return 4 + 4 + 8 + sizeof(checksum_t); } else { // header: version 1 byte, type 1 byte, flags 1 byte, // ctrl field 1 byte, length 8 bytes return 4 + 8; } } size_t serialize(gu::byte_t* buf, size_t buflen, size_t offset)const { assert(version_ >= VER21); size_t const orig_offset(offset); offset = gu::serialize1(uint8_t(version_), buf, buflen, offset); offset = gu::serialize1(uint8_t(type_), buf, buflen, offset); offset = gu::serialize1(flags_, buf, buflen, offset); offset = gu::serialize1(ctrl_, buf, buflen, offset); if (gu_likely(version_ >= VER40)) { offset = gu::serialize4(len_, buf, buflen, offset); offset = gu::serialize8(seqno_, buf, buflen, offset); *reinterpret_cast(buf + offset) = htog_checksum(buf + orig_offset, offset - orig_offset); offset += sizeof(checksum_t); } else /**/ { uint64_t const tmp(len_); offset = gu::serialize8(tmp, buf, buflen, offset); } assert(offset - orig_offset == serial_size()); return offset; } size_t unserialize(const gu::byte_t* buf, size_t buflen, size_t offset) { assert(version_ >= VER21); size_t orig_offset(offset); uint8_t u8; offset = gu::unserialize1(buf, buflen, offset, u8); if (gu_unlikely(u8 != version_)) throw_invalid_version(u8); offset = gu::unserialize1(buf, buflen, offset, u8); type_ = static_cast(u8); offset = gu::unserialize1(buf, buflen, offset, flags_); offset = gu::unserialize1(buf, buflen, offset, ctrl_); if (gu_likely(version_ >= VER40)) { offset = gu::unserialize4(buf, buflen, offset, len_); offset = gu::unserialize8(buf, buflen, offset, seqno_); checksum_t const computed(htog_checksum(buf + orig_offset, offset-orig_offset)); const checksum_t* expected (reinterpret_cast(buf + offset)); if (gu_unlikely(computed != *expected)) throw_corrupted_header(); offset += sizeof(checksum_t); } else { uint64_t tmp; offset = gu::unserialize8(buf, buflen, offset, tmp); assert(tmp < std::numeric_limits::max()); len_ = tmp; } assert(offset - orig_offset == serial_size()); return offset; } private: wsrep_seqno_t seqno_; uint32_t len_; Type type_; uint8_t version_; uint8_t flags_; int8_t ctrl_; typedef uint64_t checksum_t; // returns endian-adjusted checksum of buf static checksum_t htog_checksum(const void* const buf, size_t const size) { return gu::htog(gu::FastHash::digest(buf, size)); } void throw_invalid_version(uint8_t v); void throw_corrupted_header(); }; std::ostream& operator<< (std::ostream& os, const Message& m); class Handshake : public Message { public: Handshake(int version = -1) : Message(version, Message::T_HANDSHAKE, 0, 0, 0) { } }; class HandshakeResponse : public Message { public: HandshakeResponse(int version = -1) : Message(version, Message::T_HANDSHAKE_RESPONSE, 0, 0, 0) { } }; class Ctrl : public Message { public: enum { // negative values reserved for error codes C_OK = 0, C_EOF = 1 }; Ctrl(int version = -1, int8_t code = 0) : Message(version, Message::T_CTRL, 0, code, 0) { } }; class Ordered : public Message { public: Ordered(int version, Type type, uint8_t flags, uint32_t len, wsrep_seqno_t const seqno) : Message(version, type, flags, 0, len, seqno) { } }; class Proto { public: Proto(gcache::GCache& gc, int version, bool keep_keys) : gcache_ (gc), raw_sent_ (0), real_sent_(0), version_ (version), keep_keys_(keep_keys) { } ~Proto() { if (raw_sent_ > 0) { log_info << "ist proto finished, raw sent: " << raw_sent_ << " real sent: " << real_sent_ << " frac: " << (raw_sent_ == 0 ? 0. : static_cast(real_sent_)/raw_sent_); } } void send_handshake(gu::AsioSocket& socket) { Handshake hs(version_); gu::Buffer buf(hs.serial_size()); size_t offset(hs.serialize(&buf[0], buf.size(), 0)); size_t n(socket.write(gu::AsioConstBuffer(&buf[0], buf.size()))); if (n != offset) { gu_throw_error(EPROTO) << "error sending handshake"; } } void recv_handshake(gu::AsioSocket& socket) { Message msg(version_); gu::Buffer buf(msg.serial_size()); size_t n(socket.read(gu::AsioMutableBuffer(&buf[0], buf.size()))); if (n != buf.size()) { gu_throw_error(EPROTO) << "error receiving handshake"; } (void)msg.unserialize(&buf[0], buf.size(), 0); log_debug << "handshake msg: " << msg.version() << " " << msg.type() << " " << msg.len(); switch (msg.type()) { case Message::T_HANDSHAKE: break; case Message::T_CTRL: switch (msg.ctrl()) { case Ctrl::C_EOF: gu_throw_error(EINTR); default: gu_throw_error(EPROTO) << "unexpected ctrl code: " << msg.ctrl(); } break; default: gu_throw_error(EPROTO) << "unexpected message type: " << msg.type(); } if (msg.version() != version_) { gu_throw_error(EPROTO) << "mismatching protocol version: " << msg.version() << " required: " << version_; } // TODO: Figure out protocol versions to use } void send_handshake_response(gu::AsioSocket& socket) { HandshakeResponse hsr(version_); gu::Buffer buf(hsr.serial_size()); size_t offset(hsr.serialize(&buf[0], buf.size(), 0)); size_t n(socket.write(gu::AsioConstBuffer(&buf[0], buf.size()))); if (n != offset) { gu_throw_error(EPROTO) << "error sending handshake response"; } } void recv_handshake_response(gu::AsioSocket& socket) { Message msg(version_); gu::Buffer buf(msg.serial_size()); size_t n(socket.read(gu::AsioMutableBuffer(&buf[0], buf.size()))); if (n != buf.size()) { gu_throw_error(EPROTO) << "error receiving handshake"; } (void)msg.unserialize(&buf[0], buf.size(), 0); log_debug << "handshake response msg: " << msg.version() << " " << msg.type() << " " << msg.len(); switch (msg.type()) { case Message::T_HANDSHAKE_RESPONSE: break; case Message::T_CTRL: switch (msg.ctrl()) { case Ctrl::C_EOF: gu_throw_error(EINTR) << "interrupted by ctrl"; default: gu_throw_error(EPROTO) << "unexpected ctrl code: " << msg.ctrl(); } default: gu_throw_error(EINVAL) << "unexpected message type: " << msg.type(); } } void send_ctrl(gu::AsioSocket& socket, int8_t code) { Ctrl ctrl(version_, code); gu::Buffer buf(ctrl.serial_size()); size_t offset(ctrl.serialize(&buf[0], buf.size(), 0)); size_t n(socket.write(gu::AsioConstBuffer(&buf[0], buf.size()))); if (n != offset) { gu_throw_error(EPROTO) << "error sending ctrl message"; } } int8_t recv_ctrl(gu::AsioSocket& socket) { Message msg(version_); gu::Buffer buf(msg.serial_size()); size_t n(socket.read(gu::AsioMutableBuffer(&buf[0], buf.size()))); if (n != buf.size()) { gu_throw_error(EPROTO) << "error receiving handshake"; } (void)msg.unserialize(&buf[0], buf.size(), 0); log_debug << "msg: " << msg.version() << " " << msg.type() << " " << msg.len(); switch (msg.type()) { case Message::T_CTRL: break; default: gu_throw_error(EPROTO) << "unexpected message type: " << msg.type(); } return msg.ctrl(); } void send_ordered(gu::AsioSocket& socket, const gcache::GCache::Buffer& buffer, bool const preload_flag) { Message::Type type(ordered_type(buffer)); std::array cbs; size_t payload_size; /* size of the 2nd cbs buffer */ size_t sent; // for proto ver < VER40 compatibility int64_t seqno_d(WSREP_SEQNO_UNDEFINED); if (gu_likely(Message::T_SKIP != type)) { assert(Message::T_TRX == type || version_ >= VER40); galera::WriteSetIn ws; gu::Buf tmp = { buffer.ptr(), buffer.size() }; if (keep_keys_ || Message::T_CCHANGE == type) { payload_size = buffer.size(); const void* const ptr(buffer.ptr()); cbs[1] = gu::AsioConstBuffer(ptr, payload_size); cbs[2] = gu::AsioConstBuffer(ptr, 0); if (gu_likely(Message::T_TRX == type)) // compatibility { ws.read_header (tmp); seqno_d = buffer.seqno_g() - ws.pa_range(); assert(buffer.seqno_g() == ws.seqno()); } } else { ws.read_buf (tmp, 0); WriteSetIn::GatherVector out; payload_size = ws.gather (out, false, false); assert (2 == out->size()); cbs[1] = gu::AsioConstBuffer(out[0].ptr, out[0].size); cbs[2] = gu::AsioConstBuffer(out[1].ptr, out[1].size); seqno_d = buffer.seqno_g() - ws.pa_range(); assert(buffer.seqno_g() == ws.seqno()); } } else { assert(Message::T_SKIP == type); payload_size = 0; seqno_d = WSREP_SEQNO_UNDEFINED; /* in proto ver < VER40 everything is T_TRX */ if (gu_unlikely(version_ < VER40)) type = Message::T_TRX; } /* in version >= 3 metadata is included in Msg header, leaving * it here for backward compatibility */ size_t const trx_meta_size(version_ >= VER40 ? 0 : (8 /* seqno_g */ + 8 /* seqno_d */)); uint8_t const msg_flags((version_ >= VER40 && preload_flag) ? Message::F_PRELOAD : 0); Ordered to_msg(version_, type, msg_flags, trx_meta_size + payload_size, buffer.seqno_g()); gu::Buffer buf(to_msg.serial_size() + trx_meta_size); size_t offset(to_msg.serialize(&buf[0], buf.size(), 0)); if (gu_unlikely(version_ < VER40)) { offset = gu::serialize8(buffer.seqno_g(), &buf[0], buf.size(), offset); offset = gu::serialize8(seqno_d, &buf[0], buf.size(), offset); } cbs[0] = gu::AsioConstBuffer(&buf[0], buf.size()); if (gu_likely(payload_size)) { sent = gu::write(socket, cbs); } else { sent = socket.write(cbs[0]); } log_debug << "sent " << sent << " bytes"; } void skip_bytes(gu::AsioSocket& socket, size_t bytes) { gu::Buffer buf(4092); while (bytes > 0) { bytes -= socket.read( gu::AsioMutableBuffer( &buf[0], std::min(buf.size(), bytes))); } assert(bytes == 0); } void recv_ordered(gu::AsioSocket& socket, std::pair& ret) { gcs_action& act(ret.first); act.seqno_g = 0; // EOF // act.seqno_l has no significance act.buf = NULL; // skip act.size = 0; // skip act.type = GCS_ACT_UNKNOWN; // EOF Message msg(version_); gu::Buffer buf(msg.serial_size()); size_t n(socket.read(gu::AsioMutableBuffer(&buf[0], buf.size()))); if (n != buf.size()) { gu_throw_error(EPROTO) << "error receiving trx header"; } (void)msg.unserialize(&buf[0], buf.size(), 0); log_debug << "received header: " << n << " bytes, type " << msg.type() << " len " << msg.len(); switch (msg.type()) { case Message::T_TRX: case Message::T_CCHANGE: case Message::T_SKIP: { size_t offset(0); int64_t seqno_g(msg.seqno()); // compatibility with 3.x if (gu_unlikely(version_ < VER40)) //compatibility with 3.x { assert(msg.type() == Message::T_TRX); int64_t seqno_d; buf.resize(sizeof(seqno_g) + sizeof(seqno_d)); n = socket.read(gu::AsioMutableBuffer(&buf[0],buf.size())); if (n != buf.size()) { assert(0); gu_throw_error(EPROTO) << "error reading trx meta data"; } offset = gu::unserialize8(&buf[0],buf.size(),0,seqno_g); if (gu_unlikely(seqno_g <= 0)) { assert(0); gu_throw_error(EINVAL) << "non-positive sequence number " << seqno_g; } offset = gu::unserialize8(&buf[0], buf.size(), offset, seqno_d); if (gu_unlikely(seqno_d == WSREP_SEQNO_UNDEFINED && offset != msg.len())) { assert(0); gu_throw_error(EINVAL) << "message size " << msg.len() << " does not match expected size "<< offset; } Message::Type const type (seqno_d >= 0 ? Message::T_TRX : Message::T_SKIP); msg.set_type_seqno(type, seqno_g); } else // end compatibility with 3.x { assert(seqno_g > 0); } assert(msg.seqno() > 0); /* Backward compatibility code above could change msg type. * but it should not change below. Saving const for later * assert(). */ Message::Type const msg_type(msg.type()); gcs_act_type const gcs_type (msg_type == Message::T_CCHANGE ? GCS_ACT_CCHANGE : GCS_ACT_WRITESET); const void* wbuf; ssize_t wsize; bool already_cached(false); // Check if cert index preload trx is already in gcache. if ((msg.flags() & Message::F_PRELOAD)) { ret.second = true; try { wbuf = gcache_.seqno_get_ptr(seqno_g, wsize); skip_bytes(socket, msg.len() - offset); already_cached = true; } catch (gu::NotFound& nf) { // not found from gcache, continue as normal } } if (!already_cached) { if (gu_likely(msg_type != Message::T_SKIP)) { wsize = msg.len() - offset; void* const ptr(gcache_.malloc(wsize)); ssize_t const r (socket.read(gu::AsioMutableBuffer(ptr, wsize))); if (gu_unlikely(r != wsize)) { gu_throw_error(EPROTO) << "error reading write set data, " << "expected " << wsize << " bytes, got " << r << " bytes"; } wbuf = ptr; } else { wsize = GU_WORDSIZE/8; // bits to bytes wbuf = gcache_.malloc(wsize); } gcache_.seqno_assign(wbuf, msg.seqno(), gcs_type, msg_type == Message::T_SKIP); } assert(msg.type() == msg_type); switch(msg_type) { case Message::T_TRX: case Message::T_CCHANGE: act.buf = wbuf; // not skip act.size = wsize; // fall through case Message::T_SKIP: act.seqno_g = msg.seqno(); // not EOF act.type = gcs_type; break; default: gu_throw_error(EPROTO) << "Unrecognized message type" << msg_type; } return; } case Message::T_CTRL: switch (msg.ctrl()) { case Ctrl::C_EOF: return; default: if (msg.ctrl() >= 0) { gu_throw_error(EPROTO) << "unexpected ctrl code: " << msg.ctrl(); } else { gu_throw_error(-msg.ctrl()) << "peer reported error: " << -msg.ctrl(); } } default: gu_throw_error(EPROTO) << "unexpected message type: " << msg.type(); } gu_throw_fatal; throw; } private: gcache::GCache& gcache_; uint64_t raw_sent_; uint64_t real_sent_; int version_; bool keep_keys_; Message::Type ordered_type(const gcache::GCache::Buffer& buf) { assert(buf.type() == GCS_ACT_WRITESET || buf.type() == GCS_ACT_CCHANGE); if (gu_likely(!buf.skip())) { switch (buf.type()) { case GCS_ACT_WRITESET: return Message::T_TRX; case GCS_ACT_CCHANGE: return (version_ >= VER40 ? Message::T_CCHANGE : Message::T_SKIP); default: log_error << "Unsupported message type from cache: " << buf.type() << ". Skipping seqno " << buf.seqno_g(); assert(0); return Message::T_SKIP; } } else { return Message::T_SKIP; } } }; } } #include "gu_enable_non_virtual_dtor.hpp" #endif // GALERA_IST_PROTO_HPP galera-4-26.4.22/galera/src/galera_exception.hpp000644 000162 177776 00000005031 14755062442 022555 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2016 Codership Oy // #ifndef GALERA_EXCEPTION_HPP #define GALERA_EXCEPTION_HPP #include #include #include "wsrep_api.h" #include namespace galera { /*! * An exception to handle applier errors and avoid confusing wsrep error codes * with the standard ones */ class ApplyException : public gu::Exception { public: ApplyException (const std::string& msg, void* b1, const void* b2, size_t len) : gu::Exception(msg, -1), data_(b1), const_data_(b2), data_len_(len) { assert(NULL == b1 || NULL == b2); } ApplyException() : gu::Exception("", 0), data_(NULL), const_data_(NULL), data_len_(0) {} ApplyException(const ApplyException& ae) : gu::Exception(ae), data_(ae.data_), const_data_(ae.const_data_), data_len_(ae.data_len_) {} ~ApplyException() throw() {} /* this is just int because we must handle any positive value */ int status() const { return get_errno(); } const void* data() const { return const_data_ ? const_data_ : data_; } size_t data_len() const { return data_len_; } void free() { ::free(data_); data_ = NULL; } ApplyException& operator=(ApplyException ae) { using std::swap; #if 1 swap(static_cast(*this),static_cast(ae)); swap(this->data_, ae.data_); swap(this->const_data_, ae.const_data_); swap(this->data_len_, ae.data_len_); #else swap(*this, ae); #endif return *this; } private: void* data_; const void* const_data_; size_t data_len_; }; static inline const char* wsrep_status_str(wsrep_status_t& status) { switch (status) { case WSREP_OK: return "WSREP_OK"; case WSREP_WARNING: return "WSREP_WARNING"; case WSREP_TRX_MISSING: return "WSREP_TRX_MISSING"; case WSREP_TRX_FAIL: return "WSREP_TRX_FAIL"; case WSREP_BF_ABORT: return "WSREP_BF_ABORT"; case WSREP_CONN_FAIL: return "WSREP_CONN_FAIL"; case WSREP_NODE_FAIL: return "WSREP_NODE_FAIL"; case WSREP_FATAL: return "WSREP_FATAL"; case WSREP_NOT_IMPLEMENTED: return "WSREP_NOT_IMPLEMENTED"; default: return "(unknown code)"; } } /*! * And exception to handle replication errors */ class ReplException : public gu::Exception { public: ReplException (const std::string& msg, int err) : gu::Exception (msg, err) {} }; } #endif /* GALERA_EXCEPTION_HPP */ galera-4-26.4.22/galera/src/galera-sym.map000644 000162 177776 00000000645 14755062442 021301 0ustar00jenkinsnogroup000000 000000 { global: wsrep_loader; wsrep_interface_version; wsrep_init_allowlist_service_v1; wsrep_deinit_allowlist_service_v1; wsrep_init_event_service_v1; wsrep_deinit_event_service_v1; wsrep_init_config_service_v1; wsrep_deinit_config_service_v1; wsrep_node_isolation_mode_set_v1; wsrep_certify_v1; local: *; }; galera-4-26.4.22/galera/src/replicator_smm_params.cpp000644 000162 177776 00000017110 14755062442 023623 0ustar00jenkinsnogroup000000 000000 /* Copyright (C) 2012-2018 Codership Oy */ #include "replicator_smm.hpp" #include "gcs.hpp" #include "galera_common.hpp" #include "gu_uri.hpp" #include "write_set_ng.hpp" #include "gu_throw.hpp" const std::string galera::ReplicatorSMM::Param::base_host = "base_host"; const std::string galera::ReplicatorSMM::Param::base_port = "base_port"; const std::string galera::ReplicatorSMM::Param::base_dir = "base_dir"; static const std::string common_prefix = "repl."; const std::string galera::ReplicatorSMM::Param::commit_order = common_prefix + "commit_order"; const std::string galera::ReplicatorSMM::Param::causal_read_timeout = common_prefix + "causal_read_timeout"; const std::string galera::ReplicatorSMM::Param::proto_max = common_prefix + "proto_max"; const std::string galera::ReplicatorSMM::Param::key_format = common_prefix + "key_format"; const std::string galera::ReplicatorSMM::Param::max_write_set_size = common_prefix + "max_ws_size"; int const galera::ReplicatorSMM::MAX_PROTO_VER(11); galera::ReplicatorSMM::Defaults::Defaults() : map_() { map_.insert(Default(Param::base_port, BASE_PORT_DEFAULT)); map_.insert(Default(Param::base_dir, BASE_DIR_DEFAULT)); map_.insert(Default(Param::proto_max, gu::to_string(MAX_PROTO_VER))); map_.insert(Default(Param::key_format, "FLAT8")); map_.insert(Default(Param::commit_order, "3")); map_.insert(Default(Param::causal_read_timeout, "PT30S")); const int max_write_set_size(galera::WriteSetNG::MAX_SIZE); map_.insert(Default(Param::max_write_set_size, gu::to_string(max_write_set_size))); } const galera::ReplicatorSMM::Defaults galera::ReplicatorSMM::defaults; galera::ReplicatorSMM::InitConfig::InitConfig(gu::Config& conf, const char* const node_address, const char* const base_dir) { gu::ssl_register_params(conf); Replicator::register_params(conf); std::map::const_iterator i; for (i = defaults.map_.begin(); i != defaults.map_.end(); ++i) { if (i->second.empty()) conf.add(i->first); else conf.add(i->first, i->second); } conf.set_flags(Param::causal_read_timeout, gu::Config::Flag::type_duration); conf.set_flags(Param::max_write_set_size, gu::Config::Flag::type_integer); conf.set_flags(Param::base_dir, gu::Config::Flag::read_only); conf.set_flags(Param::base_port, gu::Config::Flag::read_only | gu::Config::Flag::type_integer); // what is would be a better protection? int const pv(gu::from_string(conf.get(Param::proto_max))); if (pv > MAX_PROTO_VER) { log_warn << "Can't set '" << Param::proto_max << "' to " << pv << ": maximum supported value is " << MAX_PROTO_VER; conf.add(Param::proto_max, gu::to_string(MAX_PROTO_VER)); } conf.add(COMMON_BASE_HOST_KEY, gu::Config::Flag::read_only); if (node_address && strlen(node_address) > 0) { gu::URI na(node_address, false); try { std::string const host = na.get_host(); if (host == "0.0.0.0" || host == "0:0:0:0:0:0:0:0" || host == "::") { gu_throw_error(EINVAL) << "Bad value for 'node_address': '" << host << '\''; } conf.set(BASE_HOST_KEY, host); } catch (gu::NotSet& e) {} try { conf.set(BASE_PORT_KEY, na.get_port()); } catch (gu::NotSet& e) {} } // Now we store directory name to conf. This directory name // could be used by other components, for example by gcomm // to find appropriate location for view state file. if (base_dir) { conf.set(BASE_DIR, base_dir); } else { conf.set(BASE_DIR, BASE_DIR_DEFAULT); } /* register variables and defaults from other modules */ gcache::GCache::register_params(conf); gcs_register_params(conf); Certification::register_params(conf); ist::register_params(conf); } galera::ReplicatorSMM::ParseOptions::ParseOptions(Replicator& repl, gu::Config& conf, const char* const opts) { if (opts) conf.parse(opts); if (conf.get(Replicator::Param::debug_log)) { gu_conf_debug_on(); } else { gu_conf_debug_off(); } #ifdef GU_DBUG_ON if (conf.is_set(galera::Replicator::Param::dbug)) { GU_DBUG_PUSH(conf.get(galera::Replicator::Param::dbug).c_str()); } else { GU_DBUG_POP(); } if (conf.is_set(galera::Replicator::Param::signal)) { gu_debug_sync_signal(conf.get(galera::Replicator::Param::signal)); } #endif /* GU_DBUG_ON */ } /* helper for param_set() below */ void galera::ReplicatorSMM::set_param (const std::string& key, const std::string& value) { if (key == Param::commit_order) { log_error << "setting '" << key << "' during runtime not allowed"; gu_throw_error(EPERM) << "setting '" << key << "' during runtime not allowed"; } else if (key == Param::causal_read_timeout) { causal_read_timeout_ = gu::datetime::Period(value); } else if (key == Param::base_host || key == Param::base_port || key == Param::base_dir || key == Param::proto_max) { // nothing to do here, these params take effect only at // provider (re)start } else if (key == Param::key_format) { trx_params_.key_format_ = KeySet::version(value); } else if (key == Param::max_write_set_size) { trx_params_.max_write_set_size_ = gu::from_string(value); } else { log_warn << "parameter '" << key << "' not found"; assert(0); throw gu::NotFound(); } } void galera::ReplicatorSMM::param_set (const std::string& key, const std::string& value) { try { #ifdef GALERA_HAVE_SSL // Param `socket.ssl_reload` is used as trigger. We should // skip checking if value is changed. if (key != gu::conf::ssl_reload) { #endif if (config_.get(key) == value) return; #ifdef GALERA_HAVE_SSL } #endif } catch (gu::NotSet&) {} bool found(false); // Note: base_host is treated separately here as it cannot have // default value known at compile time. if (defaults.map_.find(key) != defaults.map_.end() || key == Param::base_host) // is my key? { set_param (key, value); found = true; config_.set(key, value); } // this key might be for another module else if (0 != key.find(common_prefix)) { try { cert_.param_set (key, value); found = true; } catch (gu::NotFound&) {} try { gcs_.param_set (key, value); found = true; } catch (gu::NotFound&) {} try { gcache_.param_set (key, value); found = true; } catch (gu::NotFound&) {} #ifdef GALERA_HAVE_SSL try { gu::ssl_param_set (key, value, config_); found = true; } catch (gu::NotFound&) {} #endif } if (!found) throw gu::NotFound(); } std::string galera::ReplicatorSMM::param_get (const std::string& key) const { return config_.get(key); } galera-4-26.4.22/galera/src/write_set_ng.cpp000644 000162 177776 00000024240 14755062442 021733 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2013-2017 Codership Oy // #include "write_set_ng.hpp" #include #include #include #ifndef NDEBUG #include // gcache::MemOps::ALIGNMENT #endif #include namespace galera { WriteSetNG::Header::Offsets::Offsets ( int a01, int a02, int a03, int a04, int a05, int a06, int a07, int a08, int a09, int a10, int a11, int a12 ) : header_ver_ (a01), header_size_ (a02), sets_ (a03), flags_ (a04), pa_range_ (a05), last_seen_ (a06), seqno_ (a07), timestamp_ (a08), source_id_ (a09), conn_id_ (a10), trx_id_ (a11), crc_ (a12) {} WriteSetNG::Header::Offsets const WriteSetNG::Header::V3 ( V3_HEADER_VERS_OFF, V3_HEADER_SIZE_OFF, V3_SETS_OFF, V3_FLAGS_OFF, V3_PA_RANGE_OFF, V3_LAST_SEEN_OFF, V3_SEQNO_OFF, V3_TIMESTAMP_OFF, V3_SOURCE_ID_OFF, V3_CONN_ID_OFF, V3_TRX_ID_OFF, V3_CRC_OFF ); size_t WriteSetNG::Header::gather (KeySet::Version const kver, DataSet::Version const dver, bool unord, bool annot, uint16_t const flags, const wsrep_uuid_t& source, const wsrep_conn_id_t& conn, const wsrep_trx_id_t& trx, GatherVector& out) { GU_COMPILE_ASSERT(MAX_VERSION <= 15, header_version_too_big); GU_COMPILE_ASSERT(KeySet::MAX_VERSION <= 15, keyset_version_too_big); GU_COMPILE_ASSERT(DataSet::MAX_VERSION <= 3, dataset_version_too_big); assert (uint(ver_) <= MAX_VERSION); assert (uint(kver) <= KeySet::MAX_VERSION); assert (uint(dver) <= DataSet::MAX_VERSION); local_[V3_MAGIC_OFF] = MAGIC_BYTE; local_[V3_HEADER_VERS_OFF] = (version() << 4) | VER3; local_[V3_HEADER_SIZE_OFF] = size(); local_[V3_SETS_OFF] = (kver << 4) | (dver << 2) | (unord * V3_UNORD_FLAG) | (annot * V3_ANNOT_FLAG); uint16_t* const fl(reinterpret_cast(local_ + V3_FLAGS_OFF)); uint16_t* const pa(reinterpret_cast(local_ + V3_PA_RANGE_OFF)); *fl = gu::htog(flags); *pa = 0; // certified ws will have dep. window of at least 1 wsrep_uuid_t* const sc(reinterpret_cast(local_ + V3_SOURCE_ID_OFF)); *sc = source; uint64_t* const cn(reinterpret_cast(local_ + V3_CONN_ID_OFF)); uint64_t* const tx(reinterpret_cast(local_ + V3_TRX_ID_OFF)); *cn = gu::htog(conn); *tx = gu::htog(trx); gu::Buf const buf = { ptr_, size() }; out->push_back(buf); return buf.size; } void WriteSetNG::Header::finalize(wsrep_seqno_t const last_seen, int const pa_range) { assert (ptr_); assert (size_ > 0); assert (pa_range >= -1); uint16_t* const pa(reinterpret_cast(ptr_ + V3_PA_RANGE_OFF)); uint64_t* const ls(reinterpret_cast(ptr_ + V3_LAST_SEEN_OFF)); uint64_t* const ts(reinterpret_cast(ptr_ + V3_TIMESTAMP_OFF)); *pa = gu::htog(std::min(int(MAX_PA_RANGE), pa_range)); *ls = gu::htog(last_seen); *ts = gu::htog(gu_time_monotonic()); update_checksum (ptr_, size() - V3_CHECKSUM_SIZE); } void WriteSetNG::Header::set_seqno(wsrep_seqno_t const seqno, uint16_t const pa_range) { assert (ptr_); assert (size_ > 0); assert (seqno > 0); assert (wsrep_seqno_t(pa_range) <= seqno); uint16_t* const fl(reinterpret_cast(ptr_ + V3_FLAGS_OFF)); uint16_t* const pa(reinterpret_cast(ptr_ + V3_PA_RANGE_OFF)); uint64_t* const sq(reinterpret_cast(ptr_ + V3_SEQNO_OFF)); uint16_t const flags(gu::htog(*fl)); *fl = gu::htog(flags | F_CERTIFIED); // certification happened *pa = gu::htog(pa_range); // certification outcome *sq = gu::htog(seqno); update_checksum (ptr_, size() - V3_CHECKSUM_SIZE); } gu::Buf WriteSetNG::Header::copy(bool const include_keys, bool const include_unrd) const { assert (ptr_ != &local_[0]); assert (size_t(size()) <= sizeof(local_)); gu::byte_t* const lptr(&local_[0]); ::memcpy (lptr, ptr_, size_); gu::byte_t const mask(0x0c | (0xf0 * include_keys) | (0x02 * include_unrd)); lptr[V3_SETS_OFF] &= mask; // zero up versions of non-included sets update_checksum (lptr, size() - V3_CHECKSUM_SIZE); gu::Buf ret = { lptr, size_ }; return ret; } void WriteSetNG::Header::Checksum::verify (Version ver, const void* const ptr, ssize_t const hsize) { GU_COMPILE_ASSERT(Header::V3_CHECKSUM_SIZE >= int(sizeof(type_t)), checksum_type_too_long); assert (hsize > V3_CHECKSUM_SIZE); type_t check(0), hcheck(0); size_t const csize(hsize - V3_CHECKSUM_SIZE); compute (ptr, csize, check); gu::unserialize(ptr, csize, hcheck); if (gu_likely(check == hcheck)) return; gu_throw_error (EINVAL) << "Header checksum mismatch: computed " << std::hex << std::setfill('0') << std::setw(sizeof(check) << 1) << check << ", found " << std::setw(sizeof(hcheck) << 1) << hcheck; } const char WriteSetOut::keys_suffix[] = "_keys"; const char WriteSetOut::data_suffix[] = "_data"; const char WriteSetOut::unrd_suffix[] = "_unrd"; const char WriteSetOut::annt_suffix[] = "_annt"; void WriteSetIn::init (ssize_t const st) { assert(false == check_thr_); const gu::byte_t* const pptr (header_.payload()); ssize_t const psize(size_ - header_.size()); assert (psize >= 0); KeySet::Version const kver(header_.keyset_ver()); if (kver != KeySet::EMPTY) gu_trace(keys_.init (kver, pptr, psize)); assert (false == check_); assert (false == check_thr_); if (gu_likely(st > 0)) /* checksum enforced */ { if (size_ >= st) { /* buffer too big, start checksumming in background */ int const err(gu_thread_create (&check_thr_id_, NULL, checksum_thread, this)); if (gu_likely(0 == err)) { check_thr_ = true; return; } log_warn << "Starting checksum thread failed: " << err << '(' << ::strerror(err) << ')'; /* fall through to checksum in foreground */ } checksum(); gu_trace(checksum_fin()); } else /* checksum skipped, pretend it's alright */ { check_ = true; } } void WriteSetIn::checksum() { const gu::byte_t* pptr (header_.payload()); ssize_t psize(size_ - header_.size()); assert (psize >= 0); try { if (keys_.size() > 0) { gu_trace(keys_.checksum()); size_t const tmpsize(keys_.serial_size()); psize -= tmpsize; pptr += tmpsize; assert (psize >= 0); } DataSet::Version const dver(header_.dataset_ver()); if (gu_likely(dver != DataSet::EMPTY)) { assert (psize > 0); gu_trace(data_.init(dver, pptr, psize)); gu_trace(data_.checksum()); size_t const tmpsize(data_.serial_size()); psize -= tmpsize; pptr += tmpsize; assert (psize >= 0); if (header_.has_unrd()) { gu_trace(unrd_.init(dver, pptr, psize)); gu_trace(unrd_.checksum()); size_t const tmpsize(unrd_.serial_size()); psize -= tmpsize; pptr += tmpsize; assert (psize >= 0); } if (header_.has_annt()) { annt_ = new DataSetIn(); gu_trace(annt_->init(dver, pptr, psize)); // we don't care for annotation checksum - it is not a reason // to throw an exception and abort execution // gu_trace(annt_->checksum()); #ifndef NDEBUG psize -= annt_->serial_size(); #endif } } #ifndef NDEBUG assert (psize >= 0); assert (size_t(psize) < gcache::MemOps::ALIGNMENT); #endif check_ = true; } catch (std::exception& e) { log_error << e.what(); } catch (...) { log_error << "Non-standard exception in WriteSet::checksum()"; } } void WriteSetIn::write_annotation(std::ostream& os) const { annt_->rewind(); ssize_t const count(annt_->count()); for (ssize_t i = 0; os.good() && i < count; ++i) { gu::Buf abuf = annt_->next(); const char* const astr(static_cast(abuf.ptr)); if (abuf.size > 0 && astr[0] != '\0') os.write(astr, abuf.size); } } size_t WriteSetIn::gather(GatherVector& out, bool include_keys, bool include_unrd) const { if (include_keys && include_unrd) { gu::Buf buf = { header_.ptr(), size_ }; out->push_back(buf); return size_; } else { out->reserve(out->size() + 4); gu::Buf buf(header_.copy(include_keys, include_unrd)); out->push_back(buf); size_t ret(buf.size); if (include_keys) { buf = keys_.buf(); out->push_back(buf); ret += buf.size; } buf = data_.buf(); out->push_back (buf); ret += buf.size; if (include_unrd) { buf = unrd_.buf(); out->push_back(buf); ret += buf.size; } if (annotated()) { buf = annt_->buf(); out->push_back (buf); ret += buf.size; } return ret; } } } /* namespace galera */ galera-4-26.4.22/galera/src/certification.cpp000644 000162 177776 00000125621 14755062442 022072 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2023 Codership Oy // #include "certification.hpp" #include "gu_lock.hpp" #include "gu_throw.hpp" #include #include // std::for_each using namespace galera; static const bool cert_debug_on(false); #define cert_debug \ if (cert_debug_on == false) { } \ else log_info << "cert debug: " #define CERT_PARAM_LOG_CONFLICTS galera::Certification::PARAM_LOG_CONFLICTS #define CERT_PARAM_OPTIMISTIC_PA galera::Certification::PARAM_OPTIMISTIC_PA static std::string const CERT_PARAM_PREFIX("cert."); std::string const CERT_PARAM_LOG_CONFLICTS(CERT_PARAM_PREFIX + "log_conflicts"); std::string const CERT_PARAM_OPTIMISTIC_PA(CERT_PARAM_PREFIX + "optimistic_pa"); static std::string const CERT_PARAM_MAX_LENGTH (CERT_PARAM_PREFIX + "max_length"); static std::string const CERT_PARAM_LENGTH_CHECK (CERT_PARAM_PREFIX + "length_check"); static std::string const CERT_PARAM_LOG_CONFLICTS_DEFAULT("no"); static std::string const CERT_PARAM_OPTIMISTIC_PA_DEFAULT("yes"); /*** It is EXTREMELY important that these constants are the same on all nodes. *** Don't change them ever!!! ***/ static std::string const CERT_PARAM_MAX_LENGTH_DEFAULT("16384"); static std::string const CERT_PARAM_LENGTH_CHECK_DEFAULT("127"); void galera::Certification::register_params(gu::Config& cnf) { const int flags(gu::Config::Flag::type_bool); cnf.add(CERT_PARAM_LOG_CONFLICTS, CERT_PARAM_LOG_CONFLICTS_DEFAULT, flags); cnf.add(CERT_PARAM_OPTIMISTIC_PA, CERT_PARAM_OPTIMISTIC_PA_DEFAULT, flags); /* The defaults below are deliberately not reflected in conf: people * should not know about these dangerous setting unless they read RTFM. */ cnf.add(CERT_PARAM_MAX_LENGTH, gu::Config::Flag::hidden); cnf.add(CERT_PARAM_LENGTH_CHECK, gu::Config::Flag::hidden); } /* a function to get around unset defaults in ctor initialization list */ static int max_length(const gu::Config& conf) { if (conf.is_set(CERT_PARAM_MAX_LENGTH)) return conf.get(CERT_PARAM_MAX_LENGTH); else return gu::Config::from_config(CERT_PARAM_MAX_LENGTH_DEFAULT); } /* a function to get around unset defaults in ctor initialization list */ static int length_check(const gu::Config& conf) { if (conf.is_set(CERT_PARAM_LENGTH_CHECK)) return conf.get(CERT_PARAM_LENGTH_CHECK); else return gu::Config::from_config(CERT_PARAM_LENGTH_CHECK_DEFAULT); } static void report_stale_entry(const galera::Certification::CertIndexNG::value_type& ke, const galera::KeySetIn& key_set) { std::cerr << "Found stale entry for key: " << ke->key() << "\n"; key_set.rewind(); std::cerr << "Key set\n"; for (long i = 0; i < key_set.count(); ++i) { const auto& kp = key_set.next(); std::cerr << kp << "\n"; } } // Verify that there are no stale entries of ts left after index purge. // Is stale entry is found, the corresponding key and the key set is // printed into stderr. Debug build will assert. // // This method requires iterating over whole index, so it is relatively // expensive, and should be used only for debugging purposes. static void check_purge_complete(const galera::Certification::CertIndexNG& cert_index, const galera::TrxHandleSlave* ts, const galera::KeySetIn& key_set) { std::for_each( cert_index.begin(), cert_index.end(), [&key_set, ts] (const galera::Certification::CertIndexNG::value_type& ke) { ke->for_each_ref([&ke, &key_set, ts](const TrxHandleSlave* ref) { if (ts == ref) { report_stale_entry(ke, key_set); } assert(ts != ref); }); }); } // Purge key set from given index static void purge_key_set(galera::Certification::CertIndexNG& cert_index, galera::TrxHandleSlave* ts, const galera::KeySetIn& key_set, const long count) { for (long i(0); i < count; ++i) { const galera::KeySet::KeyPart& kp(key_set.next()); galera::KeyEntryNG ke(kp); galera::Certification::CertIndexNG::iterator ci(cert_index.find(&ke)); assert(ci != cert_index.end()); if (ci == cert_index.end()) { log_warn << "Could not find key from index"; continue; } galera::KeyEntryNG* const kep(*ci); assert(kep->referenced() == true); wsrep_key_type_t const p(kp.wsrep_type(ts->version())); if (kep->ref_trx(p) == ts) { kep->unref(p, ts); if (kep->referenced() == false) { cert_index.erase(ci); delete kep; } } } if (cert_debug_on) { check_purge_complete(cert_index, ts, key_set); } } void galera::Certification::purge_for_trx(TrxHandleSlave* trx) { assert(mutex_.owned()); assert(trx->version() >= 3 || trx->version() <= WriteSetNG::MAX_VERSION); const KeySetIn& keys(trx->write_set().keyset()); keys.rewind(); purge_key_set(cert_index_ng_, trx, keys, keys.count()); } /* Specifically for chain use in certify_and_depend_v3to6() */ template bool check_against(const galera::KeyEntryNG* const found, const galera::KeySet::KeyPart& key, wsrep_key_type_t const key_type, galera::TrxHandleSlave* const trx, bool const log_conflict, wsrep_seqno_t& depends_seqno) { enum CheckType { CONFLICT, DEPENDENCY, NOTHING }; static CheckType const check_table [WSREP_KEY_EXCLUSIVE+1][WSREP_KEY_EXCLUSIVE+1] = { // SH RE UP EX second / first { NOTHING, NOTHING, DEPENDENCY, DEPENDENCY }, // SH { NOTHING, NOTHING, DEPENDENCY, CONFLICT }, // RE { DEPENDENCY, DEPENDENCY, CONFLICT, CONFLICT }, // UP { CONFLICT, CONFLICT, CONFLICT, CONFLICT } // EX }; const galera::TrxHandleSlave* const ref_trx(found->ref_trx(REF_KEY_TYPE)); // trx should not have any references in index at this point assert(ref_trx != trx); bool conflict(false); if (gu_likely(0 != ref_trx)) { if (REF_KEY_TYPE == WSREP_KEY_EXCLUSIVE || REF_KEY_TYPE == WSREP_KEY_UPDATE) { cert_debug << KeySet::type(REF_KEY_TYPE) << " match: " << *trx << " <---> " << *ref_trx; } if (REF_KEY_TYPE == WSREP_KEY_SHARED || REF_KEY_TYPE == WSREP_KEY_REFERENCE || REF_KEY_TYPE == WSREP_KEY_UPDATE) assert(!ref_trx->is_toi() || trx->version() >= 6); CheckType const check_type(check_table[REF_KEY_TYPE][key_type]); switch (check_type) { case CONFLICT: // cert conflict takes place if // 1) write sets originated from different nodes, are within cert // range // 2) ref_trx is in isolation mode, write sets are within cert range // 3) Trx has not been certified yet. Already certified trxs show up // here during index rebuild. conflict = (ref_trx->global_seqno() > trx->last_seen_seqno() && (ref_trx->is_toi() || trx->source_id() != ref_trx->source_id()) && trx->certified() == false); if (gu_unlikely(cert_debug_on || (conflict && log_conflict == true))) { log_info << KeySet::type(key_type) << '-' << KeySet::type(REF_KEY_TYPE) << " trx " << (conflict ? "conflict" : "match") << " for key " << key << ": " << *trx << " <---> " << *ref_trx; } /* fall through */ case DEPENDENCY: depends_seqno = std::max(ref_trx->global_seqno(), depends_seqno); /* fall through */ case NOTHING:; } } return conflict; } /*! for convenience returns true if conflict and false if not */ static inline bool certify_and_depend_v3to6(const galera::KeyEntryNG* const found, const galera::KeySet::KeyPart& key, galera::TrxHandleSlave* const trx, bool const log_conflict) { bool ret(false); wsrep_seqno_t depends_seqno(trx->depends_seqno()); wsrep_key_type_t const key_type(key.wsrep_type(trx->version())); /* * The following cascade implements these rules: * * | ex | up | re | sh | <- horizontal axis: trx key type * ------------------------ vertical axis: found key type * ex | C | C | C | C | * ------------------------ * up | C | C | D | D | * ------------------------ C - conflict * re | C | D | N | N | D - dependency * ------------------------ N - nothing * sh | D | D | N | N | * ------------------------ * * Note that depends_seqno is an in/out parameter and is updated on every * step. */ if (check_against (found, key, key_type, trx, log_conflict, depends_seqno) || check_against (found, key, key_type, trx, log_conflict, depends_seqno) || (key_type >= WSREP_KEY_UPDATE && /* exclusive keys must be checked against shared */ (check_against (found, key, key_type, trx, log_conflict, depends_seqno) || check_against (found, key, key_type, trx, log_conflict, depends_seqno)))) { ret = true; } if (depends_seqno > trx->depends_seqno()) trx->set_depends_seqno(depends_seqno); return ret; } /* returns true on collision, false otherwise */ static bool certify_v3to6(const galera::Certification::CertIndexNG& cert_index_ng, const galera::KeySet::KeyPart& key, galera::TrxHandleSlave* const trx, bool const log_conflicts) { galera::KeyEntryNG ke(key); galera::Certification::CertIndexNG::const_iterator ci(cert_index_ng.find(&ke)); if (cert_index_ng.end() == ci) { return false; // No match } cert_debug << "found existing entry"; galera::KeyEntryNG* const kep(*ci); // Note: For we skip certification for isolated trxs, only // cert index and key_list is populated. return (!trx->is_toi() && certify_and_depend_v3to6(kep, key, trx, log_conflicts)); } // Add key to trx references for trx that passed certification. // // @param cert_index certification index in use // @param trx certified transaction // @param key_set key_set used in certification // @param key_count number of keys in key set static void do_ref_keys(galera::Certification::CertIndexNG& cert_index, galera::TrxHandleSlave* const trx, const galera::KeySetIn& key_set, const long key_count) { for (long i(0); i < key_count; ++i) { const galera::KeySet::KeyPart& k(key_set.next()); galera::KeyEntryNG ke(k); galera::Certification::CertIndexNG::const_iterator ci(cert_index.find(&ke)); if (ci == cert_index.end()) { galera::KeyEntryNG* const kep(new galera::KeyEntryNG(ke)); ci = cert_index.insert(kep).first; cert_debug << "created new entry"; } (*ci)->ref(k.wsrep_type(trx->version()), k, trx); } } galera::Certification::TestResult galera::Certification::do_test_v3to6(TrxHandleSlave* trx) { cert_debug << "BEGIN CERTIFICATION v" << trx->version() << ": " << *trx; #ifndef NDEBUG // to check that cleanup after cert failure returns cert_index // to original size size_t prev_cert_index_size(cert_index_ng_.size()); #endif // NDEBUG const KeySetIn& key_set(trx->write_set().keyset()); long const key_count(key_set.count()); long processed(0); key_set.rewind(); for (; processed < key_count; ++processed) { const KeySet::KeyPart& key(key_set.next()); if (certify_v3to6(cert_index_ng_, key, trx, log_conflicts_)) { trx->set_depends_seqno(std::max(trx->depends_seqno(), last_pa_unsafe_)); goto cert_fail; } } trx->set_depends_seqno(std::max(trx->depends_seqno(), last_pa_unsafe_)); assert (key_count == processed); key_set.rewind(); do_ref_keys(cert_index_ng_, trx, key_set, key_count); if (trx->pa_unsafe()) last_pa_unsafe_ = trx->global_seqno(); key_count_ += key_count; cert_debug << "END CERTIFICATION (success): " << *trx; return TEST_OK; cert_fail: cert_debug << "END CERTIFICATION (failed): " << *trx; assert (processed < key_count); assert(cert_index_ng_.size() == prev_cert_index_size); return TEST_FAILED; } /* Determine whether a given trx can be correctly certified under the * certification protocol currently established in the group (cert_version) * Certification protocols from 1 to 3 could only handle writesets of the same * version. Certification protocol 4 can handle writesets of both version 3 * and 4 */ static inline bool trx_cert_version_match(int const trx_version, int const cert_version) { if (cert_version <= 3) { return (trx_version == cert_version); } else { return (trx_version >= 3 && trx_version <= cert_version); } } galera::Certification::TestResult galera::Certification::do_test(const TrxHandleSlavePtr& trx) { assert(mutex_.owned()); assert(trx->source_id() != WSREP_UUID_UNDEFINED); if (!trx_cert_version_match(trx->version(), version_)) { log_warn << "trx protocol version: " << trx->version() << " does not match certification protocol version: " << version_; return TEST_FAILED; } // trx->is_certified() == true during index rebuild from IST, do_test() // must not fail, just populate index auto const cert_interval(trx->global_seqno() - trx->last_seen_seqno()); if (gu_unlikely(trx->certified() == false && (trx->last_seen_seqno() < initial_position_ || cert_interval > max_length_))) { if (cert_interval > max_length_) { log_warn << "certification interval " << cert_interval << " for trx " << *trx << " exceeds the limit of " << max_length_; } return TEST_FAILED; } TestResult res(TEST_FAILED); /* initialize parent seqno */ if (gu_unlikely(trx_map_.empty())) { trx->set_depends_seqno(trx->global_seqno() - 1); } else { if (optimistic_pa_ == false && trx->last_seen_seqno() > trx->depends_seqno()) trx->set_depends_seqno(trx->last_seen_seqno()); wsrep_seqno_t const ds(trx_map_.begin()->first - 1); if (ds > trx->depends_seqno()) trx->set_depends_seqno(ds); } switch (version_) { case 1: case 2: break; case 3: case 4: case 5: case 6: res = do_test_v3to6(trx.get()); break; default: gu_throw_fatal << "certification test for version " << version_ << " not implemented"; } if (res == TEST_OK) { ++trx_count_; gu::Lock lock(stats_mutex_); ++n_certified_; deps_dist_ += (trx->global_seqno() - trx->depends_seqno()); cert_interval_ += (trx->global_seqno() - trx->last_seen_seqno() - 1); index_size_ = cert_index_ng_.size(); } // Additional NBO certification. if (trx->flags() & TrxHandle::F_ISOLATION) { res = do_test_nbo(trx); assert(TEST_FAILED == res || trx->depends_seqno() >= 0); } byte_count_ += trx->size(); return res; } galera::Certification::TestResult galera::Certification::do_test_preordered(TrxHandleSlave* trx) { /* Source ID is not always available for preordered events (e.g. event * producer didn't provide any) so for now we must accept undefined IDs. */ //assert(trx->source_id() != WSREP_UUID_UNDEFINED); assert(trx->version() >= 3); assert(trx->preordered()); /* we don't want to go any further unless the writeset checksum is ok */ trx->verify_checksum(); // throws /* if checksum failed we need to throw ASAP, let the caller catch it, * flush monitors, save state and abort. */ /* This is a primitive certification test for preordered actions: * it does not handle gaps and relies on general apply monitor for * parallel applying. Ideally there should be a certification object * per source. */ if (gu_unlikely(last_preordered_id_ && (last_preordered_id_ + 1 != trx->trx_id()))) { log_warn << "Gap in preordered stream: source_id '" << trx->source_id() << "', trx_id " << trx->trx_id() << ", previous id " << last_preordered_id_; assert(0); } trx->set_depends_seqno(last_preordered_seqno_ + 1 - trx->write_set().pa_range()); // +1 compensates for subtracting from a previous seqno, rather than own. trx->mark_certified(); last_preordered_seqno_ = trx->global_seqno(); last_preordered_id_ = trx->trx_id(); return TEST_OK; } // // non-blocking operations // // Prepare a copy of TrxHandleSlave with private storage galera::NBOEntry copy_ts( galera::TrxHandleSlave* ts, galera::TrxHandleSlave::Pool& pool, gu::shared_ptr::type nbo_ctx) { // FIXME: Pass proper working directory from config to MappedBuffer ctor gu::shared_ptr::type buf( new galera::MappedBuffer("/tmp")); assert(ts->action().first && ts->action().second); if (ts->action().first == 0) { gu_throw_fatal << "Unassigned action pointer for transaction, cannot make a copy of: " << *ts; } buf->resize(ts->action().second); std::copy(static_cast(ts->action().first), static_cast(ts->action().first) + ts->action().second, buf->begin()); galera::TrxHandleSlaveDeleter d; gu::shared_ptr::type new_ts( galera::TrxHandleSlave::New(ts->local(), pool), d); if (buf->size() > size_t(std::numeric_limits::max())) gu_throw_error(ERANGE) << "Buffer size " << buf->size() << " out of range"; gcs_action act = {ts->global_seqno(), ts->local_seqno(), &(*buf)[0], static_cast(buf->size()), GCS_ACT_WRITESET}; if (ts->certified() == false) { // TrxHandleSlave is from group gu_trace(new_ts->unserialize(act)); } else { // TrxHandleSlave is from IST gu_trace(new_ts->unserialize(act)); } new_ts->set_local(ts->local()); return galera::NBOEntry(new_ts, buf, nbo_ctx); } static void purge_key_set_nbo(galera::Certification::CertIndexNBO& cert_index, bool is_nbo_index, galera::TrxHandleSlave* ts, const galera::KeySetIn& key_set, const long count) { using galera::Certification; using galera::KeyEntryNG; using galera::KeySet; key_set.rewind(); for (long i(0); i < count; ++i) { KeyEntryNG ke(key_set.next()); std::pair ci_range(cert_index.equal_range(&ke)); assert(std::distance(ci_range.first, ci_range.second) >= 1); wsrep_key_type_t const p(ke.key().wsrep_type(ts->version())); Certification::CertIndexNBO::iterator ci; for (ci = ci_range.first; ci != ci_range.second; ++ci) { if ((*ci)->ref_trx(p) == ts) break; } assert(ci != ci_range.second); if (ci == ci_range.second) { log_warn << "purge_key_set_nbo(): Could not find key " << ke.key() << " from NBO index, skipping"; continue; } KeyEntryNG* const kep(*ci); assert(kep->referenced() == true); kep->unref(p, ts); assert(kep->referenced() == false); cert_index.erase(ci); delete kep; } } static void end_nbo(galera::NBOMap::iterator i, galera::TrxHandleSlavePtr ts, galera::Certification::CertIndexNBO& nbo_index, galera::NBOMap& nbo_map) { NBOEntry& e(i->second); log_debug << "Ending NBO started by " << *e.ts_ptr(); // Erase entry from index const KeySetIn& key_set(e.ts_ptr()->write_set().keyset()); purge_key_set_nbo(nbo_index, true, e.ts_ptr(), key_set, key_set.count()); ts->set_ends_nbo(e.ts_ptr()->global_seqno()); nbo_map.erase(i); } gu::shared_ptr::type galera::Certification::nbo_ctx_unlocked( wsrep_seqno_t const seqno) { // This will either // * Insert a new NBOCtx shared_ptr into ctx map if one didn't exist // before, or // * Return existing entry, while newly created shared ptr gets freed // automatically when it goes out of scope return nbo_ctx_map_.insert( std::make_pair(seqno, gu::make_shared())).first->second; } gu::shared_ptr::type galera::Certification::nbo_ctx( wsrep_seqno_t const seqno) { assert(seqno > 0); gu::Lock lock(mutex_); return nbo_ctx_unlocked(seqno); } void galera::Certification::erase_nbo_ctx(wsrep_seqno_t const seqno) { assert(seqno > 0); gu::Lock lock(mutex_); size_t n_erased(nbo_ctx_map_.erase(seqno)); assert(n_erased == 1); (void)n_erased; } static bool is_exclusive(const galera::KeyEntryNG* ke) { assert(ke != 0); assert((ke->ref_trx(WSREP_KEY_SHARED) || ke->ref_trx(WSREP_KEY_REFERENCE) || ke->ref_trx(WSREP_KEY_UPDATE) || ke->ref_trx(WSREP_KEY_EXCLUSIVE)) && !(ke->ref_trx(WSREP_KEY_SHARED) && ke->ref_trx(WSREP_KEY_REFERENCE) && ke->ref_trx(WSREP_KEY_UPDATE) && ke->ref_trx(WSREP_KEY_EXCLUSIVE))); return (ke->ref_trx(WSREP_KEY_EXCLUSIVE) != 0 || ke->ref_trx(WSREP_KEY_UPDATE) != 0); } static bool certify_nbo(galera::Certification::CertIndexNBO& cert_index, const galera::KeySet::KeyPart& key, galera::TrxHandleSlave* const trx, bool const log_conflicts) { using galera::KeyEntryNG; using galera::Certification; using galera::TrxHandleSlave; KeyEntryNG ke(key); std::pair it(cert_index.equal_range(&ke)); // Certification is done over whole index as opposed to regular // write set certification where only given range is used // If found range is non-empty, it must be either single exclusive // key or all shared. assert(std::count_if(it.first, it.second, is_exclusive) == 0 || std::distance(it.first, it.second) == 1); Certification::CertIndexNBO::iterator i; if ((i = std::find_if(it.first, it.second, is_exclusive)) != cert_index.end()) { if (gu_unlikely(log_conflicts == true)) { const TrxHandleSlave* ref_trx((*i)->ref_trx(WSREP_KEY_EXCLUSIVE)); assert(ref_trx != 0); log_info << "NBO conflict for key " << key << ": " << *trx << " <--X--> " << *ref_trx; } return true; } return false; } static void do_ref_keys_nbo(galera::Certification::CertIndexNBO& index, TrxHandleSlave* const trx, const galera::KeySetIn& key_set, const long key_count) { using galera::KeySet; using galera::KeyEntryNG; using galera::Certification; key_set.rewind(); for (long i(0); i < key_count; ++i) { const KeySet::KeyPart& key(key_set.next()); wsrep_key_type_t const type(key.wsrep_type(trx->version())); KeyEntryNG* kep (new KeyEntryNG(key)); Certification::CertIndexNBO::iterator it; assert((it = index.find(kep)) == index.end() || (*it)->ref_trx(type) != trx); it = index.insert(kep); (*it)->ref(type, key, trx); } } galera::Certification::TestResult galera::Certification::do_test_nbo( const TrxHandleSlavePtr& ts) { assert(!ts->is_dummy()); assert(ts->flags() & TrxHandle::F_ISOLATION); assert(ts->flags() & (TrxHandle::F_BEGIN | TrxHandle::F_COMMIT)); if (nbo_position_ >= ts->global_seqno()) { // This is part of cert index preload, needs to be dropped since // it is already processed by this node before partitioning. assert(ts->certified() == true); // Return TEST_OK. If the trx is in index preload, it has // passed certification on donor. log_debug << "Dropping NBO " << *ts; return TEST_OK; } nbo_position_ = ts->global_seqno(); #ifndef NDEBUG size_t prev_nbo_index_size(nbo_index_.size()); #endif // NDEBUG bool ineffective(false); galera::Certification::TestResult ret(TEST_OK); if ((ts->flags() & TrxHandle::F_BEGIN) && (ts->flags() & TrxHandle::F_COMMIT)) { // Old school atomic TOI log_debug << "TOI: " << *ts; const KeySetIn& key_set(ts->write_set().keyset()); long const key_count(key_set.count()); long processed(0); key_set.rewind(); for (; processed < key_count; ++processed) { const KeySet::KeyPart& key(key_set.next()); if (certify_nbo(nbo_index_, key, ts.get(), log_conflicts_)) { ret = TEST_FAILED; break; } } log_debug << "NBO test result " << ret << " for TOI " << *ts; // Atomic TOI should not cause change in NBO index assert(prev_nbo_index_size == nbo_index_.size()); } else if (ts->flags() & TrxHandle::F_BEGIN) { // Beginning of non-blocking operation log_debug << "NBO start: " << *ts; // We need a copy of ts since the lifetime of NBO may exceed // the lifetime of the buffer in GCache NBOEntry entry(copy_ts(ts.get(), nbo_pool_, nbo_ctx_unlocked( ts->global_seqno()))); TrxHandleSlave* new_ts(entry.ts_ptr()); const KeySetIn& key_set(new_ts->write_set().keyset()); long const key_count(key_set.count()); long processed(0); key_set.rewind(); for (; processed < key_count; ++processed) { const KeySet::KeyPart& key(key_set.next()); if (certify_nbo(nbo_index_, key, new_ts, log_conflicts_)) { ret = TEST_FAILED; break; } } switch (ret) { case TEST_OK: do_ref_keys_nbo(nbo_index_, new_ts, key_set, key_count); nbo_map_.insert(std::make_pair(new_ts->global_seqno(), entry)); break; case TEST_FAILED: // Clean keys not needed here since certify_nbo() // does not insert them into nbo_index_ break; } } else { assert(ts->nbo_end()); // End of non-blocking operation log_debug << "NBO end: " << *ts; ineffective = true; NBOKey key; const DataSetIn& ws(ts->write_set().dataset()); ws.rewind(); assert(ws.count() == 1); if (ws.count() != 1) gu_throw_fatal << "Invalid dataset count in " << *ts; gu::Buf buf(ws.next()); key.unserialize(static_cast(buf.ptr), buf.size, 0); NBOMap::iterator i(nbo_map_.find(key)); if (i != nbo_map_.end()) { NBOEntry& e(i->second); e.add_ended(ts->source_id()); if (ts->local() == true) { // Clear NBO context aborted flag if it is set by // intermediate view change. e.nbo_ctx()->set_aborted(false); } if (current_view_.subset_of(e.ended_set())) { // All nodes of the current primary view have // ended the operation. end_nbo(i, ts, nbo_index_, nbo_map_); ineffective = false; } } else { log_warn << "no corresponding NBO begin found for NBO end " << *ts; } } if (gu_likely(TEST_OK == ret)) { ts->set_depends_seqno(ts->global_seqno() - 1); if (gu_unlikely(ineffective)) { assert(ts->nbo_end()); assert(ts->ends_nbo() == WSREP_SEQNO_UNDEFINED); ret = TEST_FAILED; } } assert(TEST_FAILED == ret || ts->depends_seqno() >= 0); return ret; } galera::Certification::Certification(gu::Config& conf, ServiceThd* thd) : version_ (-1), conf_ (conf), trx_map_ (), cert_index_ng_ (), nbo_map_ (), nbo_ctx_map_ (), nbo_index_ (), nbo_pool_ (sizeof(TrxHandleSlave)), deps_set_ (), current_view_ (), service_thd_ (thd), mutex_ (), trx_size_warn_count_ (0), initial_position_ (-1), position_ (-1), nbo_position_ (-1), safe_to_discard_seqno_ (-1), last_pa_unsafe_ (-1), last_preordered_seqno_ (position_), last_preordered_id_ (0), stats_mutex_ (), n_certified_ (0), deps_dist_ (0), cert_interval_ (0), index_size_ (0), key_count_ (0), byte_count_ (0), trx_count_ (0), max_length_ (max_length(conf)), max_length_check_ (length_check(conf)), inconsistent_ (false), log_conflicts_ (conf.get(CERT_PARAM_LOG_CONFLICTS)), optimistic_pa_ (conf.get(CERT_PARAM_OPTIMISTIC_PA)) {} galera::Certification::~Certification() { log_info << "cert index usage at exit " << cert_index_ng_.size(); log_info << "cert trx map usage at exit " << trx_map_.size(); log_info << "deps set usage at exit " << deps_set_.size(); double avg_cert_interval(0); double avg_deps_dist(0); size_t index_size(0); stats_get(avg_cert_interval, avg_deps_dist, index_size); log_info << "avg deps dist " << avg_deps_dist; log_info << "avg cert interval " << avg_cert_interval; log_info << "cert index size " << index_size; gu::Lock lock(mutex_); for_each(trx_map_.begin(), trx_map_.end(), PurgeAndDiscard(*this)); trx_map_.clear(); nbo_map_.clear(); std::for_each(nbo_index_.begin(), nbo_index_.end(), [](CertIndexNBO::value_type key_entry) { for (int i(0); i <= KeySet::Key::TYPE_MAX; ++i) { wsrep_key_type_t key_type(static_cast(i)); const TrxHandleSlave* ts(key_entry->ref_trx(key_type)); if (ts) { key_entry->unref(key_type, ts); } } delete key_entry; }); if (service_thd_) { service_thd_->release_seqno(position_); service_thd_->flush(gu::UUID()); } } void galera::Certification::assign_initial_position(const gu::GTID& gtid, int const version) { assert(gtid.seqno() >= 0 || gtid == gu::GTID()); switch (version) { // value -1 used in initialization when trx protocol version is not // available case -1: case 1: case 2: case 3: case 4: case 5: case 6: break; default: gu_throw_fatal << "certification/trx version " << version << " not supported"; } wsrep_seqno_t const seqno(gtid.seqno()); gu::Lock lock(mutex_); std::for_each(trx_map_.begin(), trx_map_.end(), PurgeAndDiscard(*this)); if (seqno >= position_) { assert(cert_index_ng_.size() == 0); } else { if (seqno > 0) // don't warn on index reset. { log_warn << "moving position backwards: " << position_ << " -> " << seqno; } std::for_each(cert_index_ng_.begin(), cert_index_ng_.end(), gu::DeleteObject()); cert_index_ng_.clear(); } trx_map_.clear(); assert(cert_index_ng_.empty()); if (service_thd_) { service_thd_->release_seqno(position_); service_thd_->flush(gtid.uuid()); } log_info << "####### Assign initial position for certification: " << gtid << ", protocol version: " << version; initial_position_ = seqno; position_ = seqno; safe_to_discard_seqno_ = seqno; last_pa_unsafe_ = seqno; last_preordered_seqno_ = position_; last_preordered_id_ = 0; version_ = version; } void galera::Certification::adjust_position(const View& view, const gu::GTID& gtid, int const version) { assert(gtid.uuid() != GU_UUID_NIL); assert(gtid.seqno() >= 0); gu::Lock lock(mutex_); // this assert is too strong: local ordered transactions may get canceled without // entering certification assert(position_ + 1 == seqno || 0 == position_); log_info << "####### Adjusting cert position: " << position_ << " -> " << gtid.seqno(); if (version != version_) { std::for_each(trx_map_.begin(), trx_map_.end(), PurgeAndDiscard(*this)); assert(trx_map_.empty() || trx_map_.rbegin()->first + 1 == position_); trx_map_.clear(); assert(cert_index_ng_.empty()); if (service_thd_) { service_thd_->release_seqno(position_); } } if (service_thd_) { service_thd_->flush(gtid.uuid()); } position_ = gtid.seqno(); last_pa_unsafe_ = position_; version_ = version; current_view_ = view; // Loop over NBO entries, clear state and abort waiters. NBO end waiters // must resend end messages. for (NBOMap::iterator i(nbo_map_.begin()); i != nbo_map_.end(); ++i) { NBOEntry& e(i->second); e.clear_ended(); e.nbo_ctx()->set_aborted(true); } } galera::Certification::TestResult galera::Certification::test(const TrxHandleSlavePtr& trx) { assert(mutex_.owned()); assert(trx->global_seqno() >= 0 /* && trx->local_seqno() >= 0 */); const TestResult ret(trx->preordered() ? do_test_preordered(trx.get()) : do_test(trx)); if (gu_unlikely(ret != TEST_OK)) { trx->mark_dummy(); } return ret; } wsrep_seqno_t galera::Certification::get_safe_to_discard_seqno_() const { assert(mutex_.owned()); wsrep_seqno_t retval; if (deps_set_.empty() == true) { retval = safe_to_discard_seqno_; } else { retval = (*deps_set_.begin()) - 1; } return retval; } wsrep_seqno_t galera::Certification::purge_trxs_upto_(wsrep_seqno_t const seqno, bool const handle_gcache) { assert (seqno > 0); assert(mutex_.owned()); TrxMap::iterator purge_bound(trx_map_.upper_bound(seqno)); cert_debug << "purging index up to " << seqno << ", safe to discard seqno " << get_safe_to_discard_seqno_(); assert(purge_bound == trx_map_.end() || purge_bound->first <= get_safe_to_discard_seqno_() + 1); for_each(trx_map_.begin(), purge_bound, PurgeAndDiscard(*this)); trx_map_.erase(trx_map_.begin(), purge_bound); if (handle_gcache && service_thd_) service_thd_->release_seqno(seqno); if (0 == ((trx_map_.size() + 1) % 10000)) { log_debug << "trx map after purge: length: " << trx_map_.size() << ", requested purge seqno: " << seqno << ", real purge seqno: " << trx_map_.begin()->first - 1; } return seqno; } galera::Certification::TestResult galera::Certification::append_trx(const TrxHandleSlavePtr& trx) { // explicit ROLLBACK is dummy() assert(!trx->is_dummy()); assert(trx->global_seqno() > 0 /* && trx->local_seqno() >= 0 */); assert(trx->global_seqno() > position_); #ifndef NDEBUG bool const explicit_rollback(trx->explicit_rollback()); #endif /* NDEBUG */ TestResult retval = TEST_FAILED; { gu::Lock lock(mutex_); if (gu_unlikely(trx->global_seqno() != position_ + 1)) { // this is perfectly normal if trx is rolled back just after // replication, keeping the log though log_debug << "seqno gap, position: " << position_ << " trx seqno " << trx->global_seqno(); } if (gu_unlikely((trx->last_seen_seqno() + 1) < trx_map_.begin()->first)) { /* See #733 - for now it is false positive */ cert_debug << "WARNING: last_seen_seqno is below certification index: " << trx_map_.begin()->first << " > " << trx->last_seen_seqno(); } position_ = trx->global_seqno(); if (gu_unlikely(!(position_ & max_length_check_) && (trx_map_.size() > static_cast(max_length_)))) { log_debug << "trx map size: " << trx_map_.size() << " - check if status.last_committed is incrementing"; wsrep_seqno_t trim_seqno(position_ - max_length_); wsrep_seqno_t const stds (get_safe_to_discard_seqno_()); if (trim_seqno > stds) { log_warn << "Attempt to trim certification index at " << trim_seqno << ", above safe-to-discard: " << stds; trim_seqno = stds; } else { cert_debug << "append_trx: purging index up to " << trim_seqno; } purge_trxs_upto_(trim_seqno, true); } retval = test(trx); if (trx_map_.insert( std::make_pair(trx->global_seqno(), trx)).second == false) gu_throw_fatal << "duplicate trx entry " << *trx; // trx with local seqno WSREP_SEQNO_UNDEFINED originates from // IST so deps set tracking should not be done if (trx->local_seqno() != WSREP_SEQNO_UNDEFINED) { assert(trx->last_seen_seqno() != WSREP_SEQNO_UNDEFINED); deps_set_.insert(trx->last_seen_seqno()); assert(deps_set_.size() <= trx_map_.size()); } } if (!trx->certified()) trx->mark_certified(); #ifndef NDEBUG if (explicit_rollback) { assert(trx->explicit_rollback()); assert(retval == TEST_OK); assert(trx->state() == TrxHandle::S_CERTIFYING); } #endif /* NDEBUG */ return retval; } void galera::Certification::append_dummy_preload(const TrxHandleSlavePtr& trx) { assert(trx->global_seqno() >= 0); assert(trx->global_seqno() > position_); gu::Lock lock(mutex_); /* Dummy preloads have only meta data available, not the whole write set, so modifying or accessing the trx object causes problems later on. Insert nullptr as a placeholder for seqno. */ if (not trx_map_.insert(std::make_pair(trx->global_seqno(), nullptr)) .second) { gu_throw_fatal << "duplicate trx entry in dummy preload"; } position_ = trx->global_seqno(); } wsrep_seqno_t galera::Certification::set_trx_committed(TrxHandleSlave& trx) { assert(trx.global_seqno() >= 0); assert(trx.is_committed() == false); wsrep_seqno_t ret(WSREP_SEQNO_UNDEFINED); { gu::Lock lock(mutex_); // certified trx with local seqno WSREP_SEQNO_UNDEFINED originates from // IST so deps set tracking should not be done // Local explicit rollback events bypassed certificaiton if (trx.certified() == true && trx.local_seqno() != WSREP_SEQNO_UNDEFINED && !trx.cert_bypass()) { assert(trx.last_seen_seqno() != WSREP_SEQNO_UNDEFINED); DepsSet::iterator i(deps_set_.find(trx.last_seen_seqno())); assert(i != deps_set_.end()); if (deps_set_.size() == 1) safe_to_discard_seqno_ = *i; deps_set_.erase(i); } if (gu_unlikely(index_purge_required())) { ret = get_safe_to_discard_seqno_(); } } trx.mark_committed(); return ret; } void set_boolean_parameter(bool& param, const std::string& value, const std::string& param_name, const std::string& change_msg) { try { bool const old(param); param = gu::Config::from_config(value); if (old != param) { log_info << (param ? "Enabled " : "Disabled ") << change_msg; } } catch (gu::NotFound& e) { gu_throw_error(EINVAL) << "Bad value '" << value << "' for boolean parameter '" << param_name << '\''; } } void galera::Certification::param_set(const std::string& key, const std::string& value) { if (key == Certification::PARAM_LOG_CONFLICTS) { set_boolean_parameter(log_conflicts_, value, CERT_PARAM_LOG_CONFLICTS, "logging of certification conflicts."); } else if (key == Certification::PARAM_OPTIMISTIC_PA) { set_boolean_parameter(optimistic_pa_, value, CERT_PARAM_OPTIMISTIC_PA, "\"optimistic\" parallel applying."); } else { throw gu::NotFound(); } conf_.set(key, value); } void galera::Certification::mark_inconsistent() { gu::Lock lock(mutex_); assert(!inconsistent_); inconsistent_ = true; } galera-4-26.4.22/galera/src/progress_callback.hpp000644 000162 177776 00000002236 14755062442 022730 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2021 Codership Oy // #ifndef GALERA_PROGRESS_CALLBACK_HPP #define GALERA_PROGRESS_CALLBACK_HPP #include "gu_progress.hpp" // gu::Progress::Callback #include "gu_event_service.hpp" #include "wsrep_api.h" #include #include namespace galera { template class ProgressCallback : public gu::Progress::Callback { public: ProgressCallback(wsrep_member_status_t from, wsrep_member_status_t to) : from_(from), to_(to) {} void operator()(T total, T done) { static std::string const event_name("progress"); std::ostringstream os; os << "{ \"from\": " << from_ << ", \"to\": " << to_ << ", \"total\": " << total << ", \"done\": " << done << ", \"undefined\": -1 }"; gu::EventService::callback(event_name, os.str()); } private: wsrep_member_status_t const from_; wsrep_member_status_t const to_; }; /* ProgressCallback */ } /* galera */ #endif /* GALERA_PROGRESS_CALLBACK_HPP */ galera-4-26.4.22/galera/src/key_entry_os.hpp000644 000162 177776 00000010735 14755062442 021765 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2012 Codership Oy // #ifndef GALERA_KEY_ENTRY_OS_HPP #define GALERA_KEY_ENTRY_OS_HPP #include "key_os.hpp" namespace galera { class TrxHandleSlave; class KeyEntryOS { public: KeyEntryOS(const KeyOS& row_key) : key_(row_key), ref_trx_(0), ref_full_trx_(0), ref_shared_trx_(0), ref_full_shared_trx_(0) {} template KeyEntryOS(int version, Ci begin, Ci end, uint8_t flags) : key_(version, begin, end, flags), ref_trx_(0), ref_full_trx_(0), ref_shared_trx_(0), ref_full_shared_trx_(0) {} KeyEntryOS(const KeyEntryOS& other) : key_(other.key_), ref_trx_(other.ref_trx_), ref_full_trx_(other.ref_full_trx_), ref_shared_trx_(other.ref_shared_trx_), ref_full_shared_trx_(other.ref_full_shared_trx_) {} ~KeyEntryOS() { assert(ref_trx_ == 0); assert(ref_full_trx_ == 0); assert(ref_shared_trx_ == 0); assert(ref_full_shared_trx_ == 0); } const KeyOS& get_key() const { return key_; } const KeyOS& get_key(int version) const { return key_; } void ref(TrxHandleSlave* trx, bool full_key) { #ifndef NDEBUG assert_ref(trx, full_key); #endif /* NDEBUG */ ref_trx_ = trx; if (full_key == true) { ref_full_trx_ = trx; } } void unref(TrxHandleSlave* trx, bool full_key) { assert(ref_trx_ != 0); if (ref_trx_ == trx) ref_trx_ = 0; if (full_key == true && ref_full_trx_ == trx) { ref_full_trx_ = 0; } else { #ifndef NDEBUG assert_unref(trx); #endif /* NDEBUG */ } } void ref_shared(TrxHandleSlave* trx, bool full_key) { #ifndef NDEBUG assert_ref_shared(trx, full_key); #endif /* NDEBUG */ ref_shared_trx_ = trx; if (full_key == true) { ref_full_shared_trx_ = trx; } } void unref_shared(TrxHandleSlave* trx, bool full_key) { assert(ref_shared_trx_ != 0); if (ref_shared_trx_ == trx) ref_shared_trx_ = 0; if (full_key == true && ref_full_shared_trx_ == trx) { ref_full_shared_trx_ = 0; } else { #ifndef NDEBUG assert_unref_shared(trx); #endif /* NDEBUG */ } } const TrxHandleSlave* ref_trx() const { return ref_trx_; } const TrxHandleSlave* ref_full_trx() const { return ref_full_trx_; } const TrxHandleSlave* ref_shared_trx() const { return ref_shared_trx_; } const TrxHandleSlave* ref_full_shared_trx() const { return ref_full_shared_trx_; } size_t size() const { return key_.size() + sizeof(*this); } private: void operator=(const KeyEntryOS&); KeyOS key_; TrxHandleSlave* ref_trx_; TrxHandleSlave* ref_full_trx_; TrxHandleSlave* ref_shared_trx_; TrxHandleSlave* ref_full_shared_trx_; #ifndef NDEBUG void assert_ref(TrxHandleSlave*, bool) const; void assert_unref(TrxHandleSlave*) const; void assert_ref_shared(TrxHandleSlave*, bool) const; void assert_unref_shared(TrxHandleSlave*) const; #endif /* NDEBUG */ }; class KeyEntryPtrHash { public: size_t operator()(const KeyEntryOS* const ke) const { return ke->get_key().hash(); } }; class KeyEntryPtrHashAll { public: size_t operator()(const KeyEntryOS* const ke) const { return ke->get_key().hash_with_flags(); } }; class KeyEntryPtrEqual { public: bool operator()(const KeyEntryOS* const left, const KeyEntryOS* const right) const { return left->get_key() == right->get_key(); } }; class KeyEntryPtrEqualAll { public: bool operator()(const KeyEntryOS* const left, const KeyEntryOS* const right) const { return left->get_key().equal_all(right->get_key()); } }; } #endif // GALERA_KEY_ENTRY_HPP galera-4-26.4.22/galera/src/galera_common.hpp000644 000162 177776 00000002342 14755062442 022051 0ustar00jenkinsnogroup000000 000000 /* * Copyright (C) 2012 Codership Oy */ /*! * @file common.hpp * * @brief Imports definitions from the global common.h */ #ifndef GALERA_COMMON_HPP #define GALERA_COMMON_HPP #if defined(HAVE_COMMON_H) #include #endif #include namespace galera { #if defined(HAVE_COMMON_H) static std::string const BASE_PORT_KEY(COMMON_BASE_PORT_KEY); static std::string const BASE_PORT_DEFAULT(COMMON_BASE_PORT_DEFAULT); static std::string const BASE_HOST_KEY(COMMON_BASE_HOST_KEY); static std::string const BASE_DIR(COMMON_BASE_DIR_KEY); static std::string const BASE_DIR_DEFAULT(COMMON_BASE_DIR_DEFAULT); static std::string const GALERA_STATE_FILE(COMMON_STATE_FILE); static std::string const VIEW_STATE_FILE(COMMON_VIEW_STAT_FILE); #else static std::string const BASE_PORT_KEY("base_port"); static std::string const BASE_PORT_DEFAULT("4567"); static std::string const BASE_HOST_KEY("base_host"); static std::string const BASE_DIR("base_dir"); static std::string const BASE_DIR_DEFAULT("."); static std::string const GALERA_STATE_FILE("grastate.dat"); static std::string const VIEW_STATE_FILE("gvwstate.dat"); #endif } #endif /* GALERA_COMMON_HPP */ galera-4-26.4.22/galera/src/mapped_buffer.hpp000644 000162 177776 00000003207 14755062442 022046 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010 Codership Oy // #ifndef GALERA_MAPPED_BUFFER_HPP #define GALERA_MAPPED_BUFFER_HPP #include #include "gu_buffer.hpp" namespace galera { class MappedBuffer { public: typedef gu::byte_t& reference; typedef gu::byte_t const& const_reference; typedef gu::byte_t* iterator; typedef gu::byte_t const* const_iterator; MappedBuffer(const std::string& working_dir, size_t threshold = 1 << 20); ~MappedBuffer(); reference operator[](size_t i) { return buf_[i]; } const_reference operator[](size_t i) const { return buf_[i]; } void reserve(size_t sz); void resize(size_t sz); void clear(); size_t size() const { return buf_size_; } bool empty() const { return (buf_size_ == 0); } iterator begin() { return buf_; } iterator end() { return (buf_ + buf_size_); } const_iterator begin() const { return buf_; } const_iterator end() const { return (buf_ + buf_size_); } private: MappedBuffer(const MappedBuffer&); void operator=(const MappedBuffer&); const std::string& working_dir_; // working dir for data files std::string file_; int fd_; // file descriptor size_t threshold_; // in-memory threshold gu::byte_t* buf_; // data buffer size_t buf_size_; // buffer size (inserted data size) size_t real_buf_size_; // real buffer size (allocated size) }; } #endif // GALERA_MAPPED_BUFFER_HPP galera-4-26.4.22/galera/src/galera_service_thd.hpp000644 000162 177776 00000004002 14755062442 023053 0ustar00jenkinsnogroup000000 000000 /* * Copyright (C) 2010-2015 Codership Oy */ #ifndef GALERA_SERVICE_THD_HPP #define GALERA_SERVICE_THD_HPP #include "galera_gcs.hpp" #include #include // gu::Mutex and gu::Cond namespace galera { class ServiceThd { public: ServiceThd (GcsI& gcs, gcache::GCache& gcache); ~ServiceThd (); /*! flush all ongoing operations (before processing CC) * and install new group UUID */ void flush (const gu::UUID& uuid); /*! reset to initial state before gcs (re)connect */ void reset(); /* !!! * The following methods must be invoked only within a monitor, * so that monitors drain during CC ensures that no outdated * actions are scheduled with the service thread after that. * !!! */ /*! schedule seqno to be reported as last committed */ /* report = false is to disable sending duplicate in case of error voting * that is done through a different, blocking channel */ void report_last_committed (gcs_seqno_t seqno, bool const report = true); /*! release write sets up to and including seqno */ void release_seqno (gcs_seqno_t seqno); private: static const uint32_t A_NONE; struct Data { gu::GTID last_committed_; gcs_seqno_t release_seqno_; uint32_t act_; Data() : last_committed_(), release_seqno_ (0), act_ (A_NONE) {} }; gcache::GCache& gcache_; GcsI& gcs_; gu_thread_t thd_; gu::Mutex mtx_; gu::Cond cond_; // service request condition gu::Cond flush_; // flush condition Data data_; static void* thd_func (void*); ServiceThd (const ServiceThd&); ServiceThd& operator= (const ServiceThd&); }; } #endif /* GALERA_SERVICE_THD_HPP */ galera-4-26.4.22/galera/src/trx_handle.hpp000644 000162 177776 00000113250 14755062442 021377 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2024 Codership Oy // #ifndef GALERA_TRX_HANDLE_HPP #define GALERA_TRX_HANDLE_HPP #include "write_set.hpp" #include "mapped_buffer.hpp" #include "fsm.hpp" #include "key_data.hpp" // for append_key() #include "key_entry_os.hpp" #include "write_set_ng.hpp" #include "wsrep_api.h" #include "gu_mutex.hpp" #include "gu_atomic.hpp" #include "gu_datetime.hpp" #include "gu_unordered.hpp" #include "gu_utils.hpp" #include "gu_macros.hpp" #include "gu_mem_pool.hpp" #include "gu_vector.hpp" #include "gu_shared_ptr.hpp" #include "gcs.hpp" #include "gu_limits.h" // page size stuff #include namespace galera { class NBOCtx; // forward decl static std::string const working_dir = "/tmp"; // Helper template for building FSMs. template class TransMapBuilder { public: TransMapBuilder() { } void add(typename T::State from, typename T::State to) { trans_map_.insert_unique(typename T::Transition(from, to)); } private: typename T::Fsm::TransMap& trans_map_; }; class TrxHandle { public: enum Flags { F_COMMIT = 1 << 0, F_ROLLBACK = 1 << 1, F_ISOLATION = 1 << 2, F_PA_UNSAFE = 1 << 3, F_COMMUTATIVE = 1 << 4, F_NATIVE = 1 << 5, F_BEGIN = 1 << 6, F_PREPARE = 1 << 7, F_SNAPSHOT = 1 << 8, F_IMPLICIT_DEPS = 1 << 9, /* * reserved for API extension */ F_PREORDERED = 1 << 15 // flag specific to WriteSet /* * reserved for internal use */ }; static const uint32_t TRXHANDLE_FLAGS_MASK = (1 << 15) | ((1 << 10) - 1); static const uint32_t EXPLICIT_ROLLBACK_FLAGS = F_PA_UNSAFE | F_ROLLBACK; static bool const FLAGS_MATCH_API_FLAGS = (WSREP_FLAG_TRX_END == F_COMMIT && WSREP_FLAG_ROLLBACK == F_ROLLBACK && WSREP_FLAG_ISOLATION == F_ISOLATION && WSREP_FLAG_PA_UNSAFE == F_PA_UNSAFE && WSREP_FLAG_COMMUTATIVE == F_COMMUTATIVE && WSREP_FLAG_NATIVE == F_NATIVE && WSREP_FLAG_TRX_START == F_BEGIN && WSREP_FLAG_TRX_PREPARE == F_PREPARE && WSREP_FLAG_SNAPSHOT == F_SNAPSHOT && WSREP_FLAG_IMPLICIT_DEPS == F_IMPLICIT_DEPS && int(WriteSetNG::F_PREORDERED) ==F_PREORDERED); static uint32_t wsrep_flags_to_trx_flags (uint32_t flags); static uint32_t trx_flags_to_wsrep_flags (uint32_t flags); static uint32_t ws_flags_to_trx_flags (uint32_t flags); bool is_toi() const { return ((write_set_flags_ & F_ISOLATION) != 0); } bool pa_unsafe() const { return ((write_set_flags_ & F_PA_UNSAFE) != 0); } bool preordered() const { return ((write_set_flags_ & F_PREORDERED) != 0); } bool nbo_start() const { return (is_toi() && (write_set_flags_ & F_BEGIN) != 0 && (write_set_flags_ & F_COMMIT) == 0); } bool nbo_end() const { return (is_toi() && (write_set_flags_ & F_BEGIN) == 0 && (write_set_flags_ & F_COMMIT) != 0); } typedef enum { S_EXECUTING, S_MUST_ABORT, S_ABORTING, S_REPLICATING, S_CERTIFYING, S_MUST_REPLAY, // replay S_REPLAYING, S_APPLYING, // grabbing apply monitor, applying S_COMMITTING, // grabbing commit monitor, committing changes S_ROLLING_BACK, S_COMMITTED, S_ROLLED_BACK } State; static const int num_states_ = S_ROLLED_BACK + 1; static void print_state(std::ostream&, State); void print_state_history(std::ostream&) const; class Transition { public: Transition(State const from, State const to) : from_(from), to_(to) { } State from() const { return from_; } State to() const { return to_; } bool operator==(Transition const& other) const { return (from_ == other.from_ && to_ == other.to_); } class Hash { public: size_t operator()(Transition const& tr) const { return (gu::HashValue(static_cast(tr.from_)) ^ gu::HashValue(static_cast(tr.to_))); } }; private: State from_; State to_; }; // class Transition typedef FSM Fsm; int version() const { return version_; } const wsrep_uuid_t& source_id() const { return source_id_; } wsrep_trx_id_t trx_id() const { return trx_id_; } void set_local(bool local) { local_ = local; } bool local() const { return local_; } wsrep_conn_id_t conn_id() const { return conn_id_; } void set_conn_id(wsrep_conn_id_t conn_id) { conn_id_ = conn_id; } State state() const { return state_(); } void print_set_state(State state) const; uint32_t flags() const { return write_set_flags_; } void set_flags(uint32_t flags) { write_set_flags_ = flags; } uint64_t timestamp() const { return timestamp_; } bool master() const { return master_; } virtual ~TrxHandle() {} // Force state, for testing purposes only. void force_state(State state) { state_.force(state); } protected: void print(std::ostream& os) const; void set_state(State const state, int const line) { state_.shift_to(state, line); if (state == S_EXECUTING) state_.reset_history(); } /* slave trx ctor */ TrxHandle(Fsm::TransMap* trans_map, bool local) : state_ (trans_map, S_REPLICATING), source_id_ (WSREP_UUID_UNDEFINED), conn_id_ (-1), trx_id_ (-1), timestamp_ (), version_ (-1), write_set_flags_ (0), local_ (local), master_ (false) {} /* local trx ctor */ TrxHandle(Fsm::TransMap* trans_map, const wsrep_uuid_t& source_id, wsrep_conn_id_t conn_id, wsrep_trx_id_t trx_id, int version) : state_ (trans_map, S_EXECUTING), source_id_ (source_id), conn_id_ (conn_id), trx_id_ (trx_id), timestamp_ (gu_time_calendar()), version_ (version), write_set_flags_ (F_BEGIN), local_ (true), master_ (true) {} Fsm state_; wsrep_uuid_t source_id_; wsrep_conn_id_t conn_id_; wsrep_trx_id_t trx_id_; int64_t timestamp_; int version_; uint32_t write_set_flags_; // Boolean denoting if the TrxHandle was generated locally. // Always true for TrxHandleMaster, set to true to // TrxHandleSlave if there exists TrxHandleMaster object corresponding // to TrxHandleSlave. bool local_; bool master_; // derived object type private: TrxHandle(const TrxHandle&); void operator=(const TrxHandle& other); friend class Wsdb; friend class Certification; template static inline uint32_t wsrep_flags_to_trx_flags_tmpl (uint32_t flags) { assert(0); // remove when needed uint32_t ret(0); if (flags & WSREP_FLAG_TRX_END) ret |= F_COMMIT; if (flags & WSREP_FLAG_ROLLBACK) ret |= F_ROLLBACK; if (flags & WSREP_FLAG_ISOLATION) ret |= F_ISOLATION; if (flags & WSREP_FLAG_PA_UNSAFE) ret |= F_PA_UNSAFE; if (flags & WSREP_FLAG_COMMUTATIVE) ret |= F_COMMUTATIVE; if (flags & WSREP_FLAG_NATIVE) ret |= F_NATIVE; if (flags & WSREP_FLAG_TRX_START) ret |= F_BEGIN; if (flags & WSREP_FLAG_TRX_PREPARE) ret |= F_PREPARE; return ret; } template static inline uint32_t trx_flags_to_wsrep_flags_tmpl (uint32_t flags) { assert(0); // remove when needed uint32_t ret(0); if (flags & F_COMMIT) ret |= WSREP_FLAG_TRX_END; if (flags & F_ROLLBACK) ret |= WSREP_FLAG_ROLLBACK; if (flags & F_ISOLATION) ret |= WSREP_FLAG_ISOLATION; if (flags & F_PA_UNSAFE) ret |= WSREP_FLAG_PA_UNSAFE; if (flags & F_COMMUTATIVE) ret |= WSREP_FLAG_COMMUTATIVE; if (flags & F_NATIVE) ret |= WSREP_FLAG_NATIVE; if (flags & F_BEGIN) ret |= WSREP_FLAG_TRX_START; if (flags & F_PREPARE) ret |= WSREP_FLAG_TRX_PREPARE; return ret; } template static inline uint32_t ws_flags_to_trx_flags_tmpl (uint32_t flags) { assert(0); // remove when needed uint32_t ret(0); if (flags & WriteSetNG::F_COMMIT) ret |= F_COMMIT; if (flags & WriteSetNG::F_ROLLBACK) ret |= F_ROLLBACK; if (flags & WriteSetNG::F_TOI) ret |= F_ISOLATION; if (flags & WriteSetNG::F_PA_UNSAFE) ret |= F_PA_UNSAFE; if (flags & WriteSetNG::F_COMMUTATIVE) ret |= F_COMMUTATIVE; if (flags & WriteSetNG::F_NATIVE) ret |= F_NATIVE; if (flags & WriteSetNG::F_BEGIN) ret |= F_BEGIN; if (flags & WriteSetNG::F_PREORDERED) ret |= F_PREORDERED; if (flags & WriteSetNG::F_PREPARE) ret |= F_PREPARE; return ret; } }; /* class TrxHandle */ template <> inline uint32_t TrxHandle::wsrep_flags_to_trx_flags_tmpl(uint32_t const flags) { return flags; } inline uint32_t TrxHandle::wsrep_flags_to_trx_flags (uint32_t const flags) { return wsrep_flags_to_trx_flags_tmpl(flags); } template <> inline uint32_t TrxHandle::trx_flags_to_wsrep_flags_tmpl(uint32_t flags) { return (flags & WSREP_FLAGS_MASK); } inline uint32_t TrxHandle::trx_flags_to_wsrep_flags (uint32_t const flags) { return trx_flags_to_wsrep_flags_tmpl(flags); } template <> inline uint32_t TrxHandle::ws_flags_to_trx_flags_tmpl(uint32_t flags) { return (flags & TRXHANDLE_FLAGS_MASK); } inline uint32_t TrxHandle::ws_flags_to_trx_flags (uint32_t const flags) { return ws_flags_to_trx_flags_tmpl(flags); } std::ostream& operator<<(std::ostream& os, TrxHandle::State s); class TrxHandleMaster; std::ostream& operator<<(std::ostream& os, const TrxHandleMaster& trx); class TrxHandleSlave; std::ostream& operator<<(std::ostream& os, const TrxHandleSlave& th); class TrxHandleSlave : public TrxHandle { public: typedef gu::MemPool Pool; static TrxHandleSlave* New(bool local, Pool& pool) { assert(pool.buf_size() == sizeof(TrxHandleSlave)); void* const buf(pool.acquire()); return new(buf) TrxHandleSlave(local, pool, buf); } /** * Adjust flags for backwards compatibility. * * Galera 4.x assigns some write set flags differently from * 3.x. During rolling upgrade these changes need to be * taken into account as 3.x originated write sets may not * have all flags set which are required for replicator internal * operation. The adjustment is done here in order to avoid spreading * the protocol specific changes up to stack. * * In particular the lack of F_BEGIN flag in 3.x needs to be * take care of. * * F_BEGIN - All of the write sets which originate from 3.x * (version < VER5) which have F_COMMIT flag set * must be assigned also F_BEGIN for internal operation. * This is safe because 3.x does not have SR or NBO * implemented, all transactions and TOI write sets * are self contained. * * @param version Write Set wire version * @param flags Flags from write set * * @return Adjusted write set flags compatible with current * implementation. */ static inline uint32_t fixup_write_set_flags(int version, uint32_t flags) { if (version < WriteSetNG::VER5) { if (flags & F_COMMIT) { flags |= F_BEGIN; } } return flags; } template size_t unserialize(const gcs_action& act) { assert(GCS_ACT_WRITESET == act.type); try { version_ = WriteSetNG::version(act.buf, act.size); action_ = std::make_pair(act.buf, act.size); switch (version_) { case WriteSetNG::VER3: case WriteSetNG::VER4: case WriteSetNG::VER5: case WriteSetNG::VER6: write_set_.read_buf (act.buf, act.size); assert(version_ == write_set_.version()); write_set_flags_ = fixup_write_set_flags( version_, ws_flags_to_trx_flags(write_set_.flags())); source_id_ = write_set_.source_id(); conn_id_ = write_set_.conn_id(); trx_id_ = write_set_.trx_id(); #ifndef NDEBUG write_set_.verify_checksum(); assert(source_id_ != WSREP_UUID_UNDEFINED); assert(WSREP_SEQNO_UNDEFINED == last_seen_seqno_); assert(WSREP_SEQNO_UNDEFINED == local_seqno_); assert(WSREP_SEQNO_UNDEFINED == last_seen_seqno_); #endif if (from_group) { local_seqno_ = act.seqno_l; global_seqno_ = act.seqno_g; if (write_set_flags_ & F_PREORDERED) { last_seen_seqno_ = global_seqno_ - 1; } else { last_seen_seqno_ = write_set_.last_seen(); } #ifndef NDEBUG assert(last_seen_seqno_ >= 0); if (last_seen_seqno_ >= global_seqno_) { log_fatal << "S: global: " << global_seqno_ << ", last_seen: " << last_seen_seqno_ << ", checksum: " << gu::PrintBase<>(write_set_.get_checksum()); } assert(last_seen_seqno_ < global_seqno_); #endif if (gu_likely(0 == (flags() & (TrxHandle::F_ISOLATION | TrxHandle::F_PA_UNSAFE)))) { assert(WSREP_SEQNO_UNDEFINED == depends_seqno_); if (gu_likely(version_) >= WriteSetNG::VER5) { depends_seqno_ = std::max (last_seen_seqno_ - write_set_.pa_range(), WSREP_SEQNO_UNDEFINED); } /* just in case Galera 3.x uses this don't condition it on version_ */ if (flags() & F_IMPLICIT_DEPS) { assert(last_seen_seqno_ >= depends_seqno_); depends_seqno_ = last_seen_seqno_; } } else { depends_seqno_ = global_seqno_ - 1; } } else { assert(!local_); global_seqno_ = write_set_.seqno(); if (gu_likely(!(nbo_end()))) { depends_seqno_ = global_seqno_-write_set_.pa_range(); assert(depends_seqno_ >= 0); } assert(depends_seqno_ < global_seqno_); certified_ = true; } #ifndef NDEBUG explicit_rollback_ = (write_set_flags_ == EXPLICIT_ROLLBACK_FLAGS); #endif /* NDEBUG */ timestamp_ = write_set_.timestamp(); assert(trx_id() != uint64_t(-1) || is_toi()); sanity_checks(); break; default: gu_throw_error(EPROTONOSUPPORT) <<"Unsupported WS version: " << version_; } return act.size; } catch (gu::Exception& e) { GU_TRACE(e); deserialize_error_log(e); throw; } } void verify_checksum() const /* throws */ { write_set_.verify_checksum(); } void update_stats(gu::Atomic& kc, gu::Atomic& kb, gu::Atomic& db, gu::Atomic& ub) { kc += write_set_.keyset().count(); kb += write_set_.keyset().size(); db += write_set_.dataset().size(); ub += write_set_.unrdset().size(); } bool certified() const { return certified_; } void mark_certified() { assert(!certified_); int dw(0); if (gu_likely(depends_seqno_ >= 0)) { dw = global_seqno_ - depends_seqno_; } /* make sure to not exceed original pa_range() */ assert(version_ < WriteSetNG::VER5 || last_seen_seqno_ - write_set_.pa_range() <= global_seqno_ - dw || preordered()); write_set_.set_seqno(global_seqno_, dw); certified_ = true; } void set_depends_seqno(wsrep_seqno_t const seqno_lt) { /* make sure depends_seqno_ never goes down */ assert(seqno_lt >= depends_seqno_ || seqno_lt == WSREP_SEQNO_UNDEFINED || preordered()); depends_seqno_ = seqno_lt; } void set_global_seqno(wsrep_seqno_t s) // for monitor cancellation { global_seqno_ = s; } void set_state(TrxHandle::State const state, int const line = -1) { TrxHandle::set_state(state, line); } void apply(void* recv_ctx, wsrep_apply_cb_t apply_cb, const wsrep_trx_meta_t& meta, wsrep_bool_t& exit_loop) /* throws */; bool is_committed() const { return committed_; } void mark_committed() { committed_ = true; } void unordered(void* recv_ctx, wsrep_unordered_cb_t apply_cb) const; std::pair action() const { return action_; } wsrep_seqno_t local_seqno() const { return local_seqno_; } wsrep_seqno_t global_seqno() const { return global_seqno_; } wsrep_seqno_t last_seen_seqno() const { return last_seen_seqno_; } wsrep_seqno_t depends_seqno() const { return depends_seqno_; } const WriteSetIn& write_set () const { return write_set_; } bool exit_loop() const { return exit_loop_; } void set_exit_loop(bool x) { exit_loop_ |= x; } typedef gu::UnorderedMap, KeyEntryPtrHash, KeyEntryPtrEqualAll> CertKeySet; void print(std::ostream& os) const; uint64_t get_checksum() const { return write_set_.get_checksum(); } size_t size() const { return write_set_.size(); } void set_ends_nbo(wsrep_seqno_t seqno) { ends_nbo_ = seqno; } wsrep_seqno_t ends_nbo() const { return ends_nbo_; } void mark_dummy() { set_flags(flags() | F_ROLLBACK); } // Mark action dummy and assign gcache buffer pointer. The // action size is left zero. void mark_dummy_with_action(const void* buf) { mark_dummy(); action_.first = buf; action_.second = 0; } bool is_dummy() const { return (flags() & F_ROLLBACK) && (flags() != EXPLICIT_ROLLBACK_FLAGS); } bool skip_event() const { return (flags() == F_ROLLBACK); } bool is_streaming() const { return !((flags() & F_BEGIN) && (flags() & F_COMMIT)); } void cert_bypass(bool const val) { assert(true == val); assert(false == cert_bypass_); cert_bypass_ = val; } bool cert_bypass() const { return cert_bypass_; } bool explicit_rollback() const { bool const ret(flags() == EXPLICIT_ROLLBACK_FLAGS); assert(ret == explicit_rollback_); return ret; } void mark_queued() { assert(!queued_); queued_ = true; } bool queued() const { return queued_; } protected: TrxHandleSlave(bool local, gu::MemPool& mp, void* buf) : TrxHandle (&trans_map_, local), local_seqno_ (WSREP_SEQNO_UNDEFINED), global_seqno_ (WSREP_SEQNO_UNDEFINED), last_seen_seqno_ (WSREP_SEQNO_UNDEFINED), depends_seqno_ (WSREP_SEQNO_UNDEFINED), ends_nbo_ (WSREP_SEQNO_UNDEFINED), mem_pool_ (mp), write_set_ (), buf_ (buf), action_ (static_cast(0), 0), certified_ (false), committed_ (false), exit_loop_ (false), cert_bypass_ (false), queued_ (false) #ifndef NDEBUG ,explicit_rollback_(false) #endif /* NDEBUG */ {} friend class TrxHandleMaster; friend class TransMapBuilder; friend class TrxHandleSlaveDeleter; private: static Fsm::TransMap trans_map_; wsrep_seqno_t local_seqno_; wsrep_seqno_t global_seqno_; wsrep_seqno_t last_seen_seqno_; wsrep_seqno_t depends_seqno_; wsrep_seqno_t ends_nbo_; gu::MemPool& mem_pool_; WriteSetIn write_set_; void* const buf_; std::pair action_; bool certified_; bool committed_; bool exit_loop_; bool cert_bypass_; bool queued_; #ifndef NDEBUG bool explicit_rollback_; #endif /* NDEBUG */ TrxHandleSlave(const TrxHandleSlave&); void operator=(const TrxHandleSlave& other); ~TrxHandleSlave() { #ifndef NDEBUG if (explicit_rollback_) assert (flags() == EXPLICIT_ROLLBACK_FLAGS); #endif /* NDEBUG */ } void destroy_local(void* ptr); void sanity_checks() const; void deserialize_error_log(const gu::Exception& e) const; }; /* TrxHandleSlave */ typedef gu::shared_ptr::type TrxHandleSlavePtr; class TrxHandleSlaveDeleter { public: void operator()(TrxHandleSlave* ptr) { gu::MemPool& mp(ptr->mem_pool_); ptr->~TrxHandleSlave(); mp.recycle(ptr); } }; class TrxHandleMaster : public TrxHandle { public: /* signed int here is to detect SIZE < sizeof(TrxHandle) */ static size_t LOCAL_STORAGE_SIZE() { static size_t const ret(gu_page_size_multiple(1 << 13 /* 8Kb */)); return ret; } struct Params { std::string working_dir_; int version_; KeySet::Version key_format_; gu::RecordSet::Version record_set_ver_; int max_write_set_size_; Params (const std::string& wdir, int ver, KeySet::Version kformat, gu::RecordSet::Version rsv = gu::RecordSet::VER2, int max_write_set_size = WriteSetNG::MAX_SIZE) : working_dir_ (wdir), version_ (ver), key_format_ (kformat), record_set_ver_ (rsv), max_write_set_size_(max_write_set_size) {} Params () : working_dir_(), version_(), key_format_(), record_set_ver_(), max_write_set_size_() {} }; static const Params Defaults; typedef gu::MemPool Pool; static TrxHandleMaster* New(Pool& pool, const Params& params, const wsrep_uuid_t& source_id, wsrep_conn_id_t conn_id, wsrep_trx_id_t trx_id) { size_t const buf_size(pool.buf_size()); assert(buf_size >= (sizeof(TrxHandleMaster) + sizeof(WriteSetOut))); void* const buf(pool.acquire()); return new(buf) TrxHandleMaster(pool, params, source_id, conn_id, trx_id, buf_size); } void lock() { mutex_.lock(); } #ifndef NDEBUG bool locked() { return mutex_.locked(); } bool owned() { return mutex_.owned(); } #endif /* NDEBUG */ void unlock() { assert(locked()); assert(owned()); mutex_.unlock(); } void set_state(TrxHandle::State const s, int const line = -1) { assert(locked()); assert(owned()); TrxHandle::set_state(s, line); } long gcs_handle() const { return gcs_handle_; } void set_gcs_handle(long gcs_handle) { gcs_handle_ = gcs_handle; } void set_flags(uint32_t const flags) // wsrep flags { TrxHandle::set_flags(flags); uint16_t ws_flags(WriteSetNG::wsrep_flags_to_ws_flags(flags)); write_set_out().set_flags(ws_flags); } void append_key(const KeyData& key) { /*! protection against protocol change during trx lifetime */ if (key.proto_ver != version()) { gu_throw_error(EINVAL) << "key version '" << key.proto_ver << "' does not match to trx version' " << version() << "'"; } gu_trace(write_set_out().append_key(key)); } void append_data(const void* data, const size_t data_len, wsrep_data_type_t type, bool store) { switch (type) { case WSREP_DATA_ORDERED: gu_trace(write_set_out().append_data(data, data_len, store)); break; case WSREP_DATA_UNORDERED: gu_trace(write_set_out().append_unordered(data, data_len,store)); break; case WSREP_DATA_ANNOTATION: gu_trace(write_set_out().append_annotation(data,data_len,store)); break; }; } bool empty() const { return write_set_out().is_empty(); } TrxHandleSlavePtr ts() { return ts_; } void reset_ts() { ts_ = TrxHandleSlavePtr(); } size_t gather(WriteSetNG::GatherVector& out) { set_ws_flags(); return write_set_out().gather(source_id(),conn_id(),trx_id(),out); } void finalize(wsrep_seqno_t last_seen_seqno) { assert(last_seen_seqno >= 0); assert(ts_ == 0 || last_seen_seqno >= ts_->last_seen_seqno()); int pa_range(pa_range_default()); if (gu_unlikely((flags() & TrxHandle::F_BEGIN) == 0 && (flags() & TrxHandle::F_ISOLATION) == 0)) { /* make sure this fragment depends on the previous */ wsrep_seqno_t prev_seqno(last_ts_seqno_); if (prev_seqno == WSREP_SEQNO_UNDEFINED) { assert((flags() & TrxHandle::F_COMMIT) || (flags() & TrxHandle::F_ROLLBACK)); prev_seqno = 0; } assert(version() >= WriteSetNG::VER5); assert(prev_seqno >= 0); // Although commit happens in order, the release of apply // monitor which is used to determine last committed may // not happen in order. Therefore it is possible that the // last seen given as an argument for this method lies // below prev_seqno. Adjust last seen seqno to match // at least prev_seqno which is now known to be committed. last_seen_seqno = std::max(last_seen_seqno, prev_seqno); pa_range = std::min(wsrep_seqno_t(pa_range), last_seen_seqno - prev_seqno); } else { assert(ts_ == 0); assert(flags() & TrxHandle::F_ISOLATION || (flags() & TrxHandle::F_ROLLBACK) == 0); } write_set_out().finalize(last_seen_seqno, pa_range); } /* Serializes wiriteset into a single buffer (for unit test purposes) */ void serialize(wsrep_seqno_t const last_seen, std::vector& ret) { set_ws_flags(); write_set_out().serialize(ret, source_id(), conn_id(), trx_id(), last_seen, pa_range_default()); } void clear() { release_write_set_out(); } void add_replicated(TrxHandleSlavePtr ts) { assert(locked()); if ((write_set_flags_ & TrxHandle::F_ISOLATION) == 0) { write_set_flags_ &= ~TrxHandle::F_BEGIN; write_set_flags_ &= ~TrxHandle::F_PREPARE; } ts_ = ts; last_ts_seqno_ = ts_->global_seqno(); } WriteSetOut& write_set_out() { /* WriteSetOut is a temporary object needed only at the writeset * collection stage. Since it may allocate considerable resources * we dont't want it to linger as long as TrxHandle is needed and * want to destroy it ASAP. So it is constructed in the buffer * allocated by TrxHandle::New() immediately following this object */ if (gu_unlikely(!wso_)) init_write_set_out(); assert(wso_); return *static_cast(wso_buf()); } void release_write_set_out() { if (gu_likely(wso_)) { write_set_out().~WriteSetOut(); wso_ = false; } } void set_deferred_abort(bool deferred_abort) { deferred_abort_ = deferred_abort; } bool deferred_abort() const { return deferred_abort_; } void print(std::ostream& os) const; private: inline int pa_range_default() const { return (version() >= WriteSetNG::VER5 ? WriteSetNG::MAX_PA_RANGE :0); } inline void set_ws_flags() { uint32_t const wsrep_flags(trx_flags_to_wsrep_flags(flags())); uint16_t const ws_flags (WriteSetNG::wsrep_flags_to_ws_flags(wsrep_flags)); write_set_out().set_flags(ws_flags); } void init_write_set_out() { assert(!wso_); assert(wso_buf_size_ >= sizeof(WriteSetOut)); gu::byte_t* const wso(static_cast(wso_buf())); gu::byte_t* const store(wso + sizeof(WriteSetOut)); assert(params_.version_ >= 0 && params_.version_ <= WriteSetNG::MAX_VERSION); new (wso) WriteSetOut (params_.working_dir_, trx_id(), params_.key_format_, store, wso_buf_size_ - sizeof(WriteSetOut), 0, params_.record_set_ver_, WriteSetNG::Version(params_.version_), DataSet::MAX_VERSION, DataSet::MAX_VERSION, params_.max_write_set_size_); wso_ = true; } const WriteSetOut& write_set_out() const { return const_cast(this)->write_set_out(); } TrxHandleMaster(gu::MemPool& mp, const Params& params, const wsrep_uuid_t& source_id, wsrep_conn_id_t conn_id, wsrep_trx_id_t trx_id, size_t reserved_size) : TrxHandle(&trans_map_, source_id, conn_id, trx_id, params.version_), mutex_ (), mem_pool_ (mp), params_ (params), ts_ (), wso_buf_size_ (reserved_size - sizeof(*this)), gcs_handle_ (-1), wso_ (false), last_ts_seqno_ (WSREP_SEQNO_UNDEFINED), deferred_abort_ (false) { assert(reserved_size > sizeof(*this) + 1024); } void* wso_buf() { return static_cast(this + 1); } ~TrxHandleMaster() { release_write_set_out(); } gu::Mutex mutex_; gu::MemPool& mem_pool_; static Fsm::TransMap trans_map_; Params const params_; TrxHandleSlavePtr ts_; // current fragment handle size_t const wso_buf_size_; int gcs_handle_; bool wso_; wsrep_seqno_t last_ts_seqno_; bool deferred_abort_; friend class TrxHandle; friend class TrxHandleSlave; friend class TrxHandleMasterDeleter; friend class TransMapBuilder; // overrides TrxHandleMaster(const TrxHandleMaster&); TrxHandleMaster& operator=(const TrxHandleMaster&); }; typedef gu::shared_ptr::type TrxHandleMasterPtr; class TrxHandleMasterDeleter { public: void operator()(TrxHandleMaster* ptr) { gu::MemPool& mp(ptr->mem_pool_); ptr->~TrxHandleMaster(); mp.recycle(ptr); } }; class TrxHandleLock { public: TrxHandleLock(TrxHandleMaster& trx) : trx_(trx) , locked_(false) { trx_.lock(); locked_ = true; } ~TrxHandleLock() { if (locked_) { trx_.unlock(); } } void lock() { trx_.lock(); locked_ = true; } void unlock() { assert(locked_ = true); locked_ = false; trx_.unlock(); } private: TrxHandleLock(const TrxHandleLock&); TrxHandleLock& operator=(const TrxHandleLock&); TrxHandleMaster& trx_; bool locked_; }; /* class TrxHnadleLock */ } /* namespace galera*/ #endif // GALERA_TRX_HANDLE_HPP galera-4-26.4.22/galera/src/data_set.cpp000644 000162 177776 00000000130 14755062442 021016 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2013 Codership Oy // #include "data_set.hpp" galera-4-26.4.22/galera/src/replicator_smm.cpp000644 000162 177776 00000325745 14755062442 022300 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2021 Codership Oy // #include "galera_common.hpp" #include "replicator_smm.hpp" #include "gcs_action_source.hpp" #include "gcs_error.hpp" #include "galera_exception.hpp" #include "galera_info.hpp" #include #include #include #include #define TX_SET_STATE(t_,s_) (t_).set_state(s_, __LINE__) wsrep_cap_t galera::ReplicatorSMM::capabilities(int protocol_version) { static uint64_t const v4_caps(WSREP_CAP_MULTI_MASTER | WSREP_CAP_CERTIFICATION | WSREP_CAP_PARALLEL_APPLYING | WSREP_CAP_TRX_REPLAY | WSREP_CAP_ISOLATION | WSREP_CAP_PAUSE | WSREP_CAP_CAUSAL_READS); static uint64_t const v5_caps(WSREP_CAP_INCREMENTAL_WRITESET | WSREP_CAP_UNORDERED | WSREP_CAP_PREORDERED); static uint64_t const v8_caps(WSREP_CAP_STREAMING); static uint64_t const v9_caps(WSREP_CAP_NBO); if (protocol_version == -1) return 0; assert(protocol_version >= 4); uint64_t caps(v4_caps); if (protocol_version >= 5) caps |= v5_caps; if (protocol_version >= 8) caps |= v8_caps; if (protocol_version >= 9) caps |= v9_caps; return caps; } std::ostream& galera::operator<<(std::ostream& os, ReplicatorSMM::State state) { switch (state) { case ReplicatorSMM::S_DESTROYED: return (os << "DESTROYED"); case ReplicatorSMM::S_CLOSED: return (os << "CLOSED"); case ReplicatorSMM::S_CONNECTED: return (os << "CONNECTED"); case ReplicatorSMM::S_JOINING: return (os << "JOINING"); case ReplicatorSMM::S_JOINED: return (os << "JOINED"); case ReplicatorSMM::S_SYNCED: return (os << "SYNCED"); case ReplicatorSMM::S_DONOR: return (os << "DONOR"); } gu_throw_fatal << "invalid state " << static_cast(state); } ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // Public ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// galera::ReplicatorSMM::ReplicatorSMM(const struct wsrep_init_args* args) : ist_event_queue_ (), init_lib_ (reinterpret_cast(args->logger_cb)), config_ (), init_config_ (config_, args->node_address, args->data_dir), parse_options_ (*this, config_, args->options), init_ssl_ (config_), protocol_version_ (-1), proto_max_ (gu::from_string(config_.get(Param::proto_max))), state_ (S_CLOSED), closing_mutex_ (), closing_cond_ (), closing_ (false), sst_state_ (SST_NONE), co_mode_ (CommitOrder::from_string( config_.get(Param::commit_order))), state_file_ (config_.get(BASE_DIR)+'/'+GALERA_STATE_FILE), st_ (state_file_), safe_to_bootstrap_ (true), trx_params_ (config_.get(BASE_DIR), -1, KeySet::version(config_.get(Param::key_format)), TrxHandleMaster::Defaults.record_set_ver_, gu::from_string(config_.get( Param::max_write_set_size))), uuid_ (WSREP_UUID_UNDEFINED), state_uuid_ (WSREP_UUID_UNDEFINED), state_uuid_str_ (), cc_seqno_ (WSREP_SEQNO_UNDEFINED), cc_lowest_trx_seqno_(WSREP_SEQNO_UNDEFINED), pause_seqno_ (WSREP_SEQNO_UNDEFINED), app_ctx_ (args->app_ctx), connected_cb_ (args->connected_cb), view_cb_ (args->view_cb), sst_request_cb_ (args->sst_request_cb), apply_cb_ (args->apply_cb), unordered_cb_ (args->unordered_cb), sst_donate_cb_ (args->sst_donate_cb), synced_cb_ (args->synced_cb), sst_donor_ (), sst_uuid_ (WSREP_UUID_UNDEFINED), sst_seqno_ (WSREP_SEQNO_UNDEFINED), sst_mutex_ (), sst_cond_ (), sst_retry_sec_ (1), sst_received_ (false), gcache_progress_cb_ (ProgressCallback(WSREP_MEMBER_UNDEFINED, WSREP_MEMBER_UNDEFINED)), gcache_ (&gcache_progress_cb_, config_, config_.get(BASE_DIR)), joined_progress_cb_ (ProgressCallback(WSREP_MEMBER_JOINED, WSREP_MEMBER_SYNCED)), gcs_ (config_, gcache_, &joined_progress_cb_, proto_max_, args->proto_ver, args->node_name, args->node_incoming), service_thd_ (gcs_, gcache_), slave_pool_ (sizeof(TrxHandleSlave), 1024, "TrxHandleSlave"), as_ (new GcsActionSource(slave_pool_, gcs_, *this,gcache_)), ist_progress_cb_ (ProgressCallback(WSREP_MEMBER_JOINER, WSREP_MEMBER_JOINED)), ist_receiver_ (config_, gcache_, slave_pool_, *this, args->node_address, &ist_progress_cb_), ist_senders_ (gcache_), wsdb_ (), cert_ (config_, &service_thd_), pending_cert_queue_ (gcache_), local_monitor_ (), apply_monitor_ (), commit_monitor_ (), causal_read_timeout_(config_.get(Param::causal_read_timeout)), receivers_ (), replicated_ (), replicated_bytes_ (), keys_count_ (), keys_bytes_ (), data_bytes_ (), unrd_bytes_ (), local_commits_ (), local_rollbacks_ (), local_cert_failures_(), local_replays_ (), causal_reads_ (), preordered_id_ (), incoming_list_ (""), incoming_mutex_ (), wsrep_stats_ () { // @todo add guards (and perhaps actions) state_.add_transition(Transition(S_CLOSED, S_DESTROYED)); state_.add_transition(Transition(S_CLOSED, S_CONNECTED)); state_.add_transition(Transition(S_CONNECTED, S_CLOSED)); state_.add_transition(Transition(S_CONNECTED, S_CONNECTED)); state_.add_transition(Transition(S_CONNECTED, S_JOINING)); // the following is possible only when bootstrapping new cluster // (trivial wsrep_cluster_address) state_.add_transition(Transition(S_CONNECTED, S_JOINED)); // the following are possible on PC remerge state_.add_transition(Transition(S_CONNECTED, S_DONOR)); state_.add_transition(Transition(S_CONNECTED, S_SYNCED)); state_.add_transition(Transition(S_JOINING, S_CLOSED)); // the following is possible if one non-prim conf follows another state_.add_transition(Transition(S_JOINING, S_CONNECTED)); state_.add_transition(Transition(S_JOINING, S_JOINED)); state_.add_transition(Transition(S_JOINED, S_CLOSED)); state_.add_transition(Transition(S_JOINED, S_CONNECTED)); state_.add_transition(Transition(S_JOINED, S_SYNCED)); // the following is possible if one desync() immediately follows another state_.add_transition(Transition(S_JOINED, S_DONOR)); state_.add_transition(Transition(S_SYNCED, S_CLOSED)); state_.add_transition(Transition(S_SYNCED, S_CONNECTED)); state_.add_transition(Transition(S_SYNCED, S_DONOR)); state_.add_transition(Transition(S_DONOR, S_CLOSED)); state_.add_transition(Transition(S_DONOR, S_CONNECTED)); state_.add_transition(Transition(S_DONOR, S_JOINED)); local_monitor_.set_initial_position(WSREP_UUID_UNDEFINED, 0); wsrep_uuid_t uuid; wsrep_seqno_t seqno; st_.get (uuid, seqno, safe_to_bootstrap_); if (0 != args->state_id && args->state_id->uuid != WSREP_UUID_UNDEFINED && args->state_id->uuid == uuid && seqno == WSREP_SEQNO_UNDEFINED) { /* non-trivial recovery information provided on startup, and db is safe * so use recovered seqno value */ seqno = args->state_id->seqno; } if (seqno >= 0) // non-trivial starting position { assert(uuid != WSREP_UUID_UNDEFINED); cc_seqno_ = seqno; // is it needed here? log_debug << "ReplicatorSMM() initial position: " << uuid << ':' << seqno; set_initial_position(uuid, seqno); cert_.assign_initial_position(gu::GTID(uuid, seqno), trx_params_.version_); gcache_.seqno_reset(gu::GTID(uuid, seqno)); // update gcache position to one supplied by app. } build_stats_vars(wsrep_stats_); } void galera::ReplicatorSMM::start_closing() { assert(closing_mutex_.locked()); assert(state_() >= S_CONNECTED); if (!closing_) { closing_ = true; gcs_.close(); } } void galera::ReplicatorSMM::shift_to_CLOSED() { assert(closing_mutex_.locked()); assert(closing_); state_.shift_to(S_CLOSED); if (state_uuid_ != WSREP_UUID_UNDEFINED) { st_.set (state_uuid_, last_committed(), safe_to_bootstrap_); } /* Cleanup for re-opening. */ uuid_ = WSREP_UUID_UNDEFINED; closing_ = false; if (st_.corrupt()) { /* this is a synchronization hack to make sure all receivers are done * with their work and won't access cert module any more. The usual * monitor drain is not enough here. */ while (receivers_() > 1) usleep(1000); // this should erase the memory of a pre-existing state. set_initial_position(WSREP_UUID_UNDEFINED, WSREP_SEQNO_UNDEFINED); cert_.assign_initial_position(gu::GTID(GU_UUID_NIL, -1), trx_params_.version_); sst_uuid_ = WSREP_UUID_UNDEFINED; sst_seqno_ = WSREP_SEQNO_UNDEFINED; cc_seqno_ = WSREP_SEQNO_UNDEFINED; cc_lowest_trx_seqno_ = WSREP_SEQNO_UNDEFINED; pause_seqno_ = WSREP_SEQNO_UNDEFINED; } closing_cond_.broadcast(); } void galera::ReplicatorSMM::wait_for_CLOSED(gu::Lock& lock) { assert(closing_mutex_.locked()); assert(closing_); while (state_() > S_CLOSED) lock.wait(closing_cond_); assert(!closing_); assert(WSREP_UUID_UNDEFINED == uuid_); } galera::ReplicatorSMM::~ReplicatorSMM() { log_info << "dtor state: " << state_(); gu::Lock lock(closing_mutex_); switch (state_()) { case S_CONNECTED: case S_JOINING: case S_JOINED: case S_SYNCED: case S_DONOR: start_closing(); wait_for_CLOSED(lock); // fall through case S_CLOSED: ist_senders_.cancel(); break; case S_DESTROYED: break; } delete as_; } wsrep_status_t galera::ReplicatorSMM::connect(const std::string& cluster_name, const std::string& cluster_url, const std::string& state_donor, bool const bootstrap) { sst_donor_ = state_donor; service_thd_.reset(); // make sure there was a proper initialization/cleanup assert(WSREP_UUID_UNDEFINED == uuid_); ssize_t err = 0; wsrep_status_t ret(WSREP_OK); wsrep_seqno_t const seqno(last_committed()); wsrep_uuid_t const gcs_uuid(seqno < 0 ? WSREP_UUID_UNDEFINED :state_uuid_); gu::GTID const inpos(gcs_uuid, seqno); log_info << "Setting GCS initial position to " << inpos; if ((bootstrap == true || cluster_url == "gcomm://") && safe_to_bootstrap_ == false) { log_error << "It may not be safe to bootstrap the cluster from this node. " << "It was not the last one to leave the cluster and may " << "not contain all the updates. To force cluster bootstrap " << "with this node, edit the grastate.dat file manually and " << "set safe_to_bootstrap to 1 ."; ret = WSREP_NODE_FAIL; } if (ret == WSREP_OK && (err = gcs_.set_initial_position(inpos)) != 0) { log_error << "gcs init failed:" << gcs_error_str(-err); ret = WSREP_NODE_FAIL; } if (ret == WSREP_OK && (err = gcs_.connect(cluster_name, cluster_url, bootstrap)) != 0) { log_error << "gcs connect failed: " << gcs_error_str(-err); ret = WSREP_NODE_FAIL; } if (ret == WSREP_OK) { state_.shift_to(S_CONNECTED); } return ret; } wsrep_status_t galera::ReplicatorSMM::close() { gu::Lock lock(closing_mutex_); if (state_() > S_CLOSED) { start_closing(); wait_for_CLOSED(lock); } return WSREP_OK; } wsrep_status_t galera::ReplicatorSMM::async_recv(void* recv_ctx) { if (state_() <= S_CLOSED) { log_error <<"async recv cannot start, provider in CLOSED state"; return WSREP_FATAL; } ++receivers_; bool exit_loop(false); wsrep_status_t retval(WSREP_OK); while (WSREP_OK == retval && state_() > S_CLOSED) { GU_DBUG_SYNC_EXECUTE("before_async_recv_process_sync", sleep(5);); ssize_t rc; while (gu_unlikely((rc = as_->process(recv_ctx, exit_loop)) == -ECANCELED)) { recv_IST(recv_ctx); // hack: prevent fast looping until ist controlling thread // resumes gcs prosessing usleep(10000); } if (gu_unlikely(rc <= 0)) { if (GcsActionSource::INCONSISTENCY_CODE == rc) { st_.mark_corrupt(); retval = WSREP_FATAL; } else { retval = WSREP_CONN_FAIL; } } else if (gu_unlikely(exit_loop == true)) { assert(WSREP_OK == retval); if (receivers_.sub_and_fetch(1) > 0) { log_info << "Slave thread exiting on request."; break; } ++receivers_; log_warn << "Refusing exit for the last slave thread."; } } /* exiting loop already did proper checks */ if (!exit_loop && receivers_.sub_and_fetch(1) == 0) { gu::Lock lock(closing_mutex_); if (state_() > S_CLOSED && !closing_) { assert(WSREP_CONN_FAIL == retval); /* Last recv thread exiting due to error but replicator is not * closed. We need to at least gracefully leave the cluster.*/ if (WSREP_OK == retval) { log_warn << "Broken shutdown sequence, provider state: " << state_() << ", retval: " << retval; assert (0); } start_closing(); // Generate zero view before exit to notify application gcs_act_cchange const cc; wsrep_uuid_t tmp(uuid_); wsrep_view_info_t* const err_view (galera_view_info_create(cc, capabilities(cc.repl_proto_ver), -1, tmp)); view_cb_(app_ctx_, recv_ctx, err_view, 0, 0); free(err_view); shift_to_CLOSED(); } } log_debug << "Slave thread exit. Return code: " << retval; return retval; } void galera::ReplicatorSMM::apply_trx(void* recv_ctx, TrxHandleSlave& ts) { assert(ts.global_seqno() > 0); assert(!ts.is_committed()); if (!ts.skip_event()) { assert(ts.trx_id() != uint64_t(-1) || ts.is_toi()); assert(ts.certified() /*Repl*/ || ts.preordered() /*IST*/); assert(ts.local() == false || ts.nbo_end() || (ts.flags() & TrxHandle::F_COMMIT) || (ts.flags() & TrxHandle::F_ROLLBACK)); assert(ts.nbo_end() == false || ts.is_dummy()); } ApplyException ae; ApplyOrder ao(ts); TX_SET_STATE(ts, TrxHandle::S_APPLYING); gu_trace(apply_monitor_.enter(ao)); if (gu_unlikely(ts.nbo_start() == true)) { // Non-blocking operation start, mark state unsafe. st_.mark_unsafe(); } wsrep_trx_meta_t meta = { { state_uuid_, ts.global_seqno() }, { ts.source_id(), ts.trx_id(), ts.conn_id() }, ts.depends_seqno() }; if (ts.is_toi()) { log_debug << "Executing TO isolated action: " << ts; st_.mark_unsafe(); } wsrep_bool_t exit_loop(false); try { gu_trace(ts.apply(recv_ctx, apply_cb_, meta, exit_loop)); } catch (ApplyException& e) { assert(0 != e.status()); assert(NULL != e.data() || 0 == e.data_len()); assert(0 != e.data_len() || NULL == e.data()); if (!st_.corrupt()) { assert(0 == e.data_len()); /* non-empty error must be handled in handle_apply_error(), while * still in commit monitor. */ on_inconsistency(); } } /* at this point any other exception is fatal, not catching anything else.*/ if (ts.local() == false) { GU_DBUG_SYNC_WAIT("after_commit_slave_sync"); } wsrep_seqno_t const safe_to_discard(cert_.set_trx_committed(ts)); /* For now need to keep it inside apply monitor to ensure all processing * ends by the time monitors are drained because of potential gcache * cleanup (and loss of the writeset buffer). Perhaps unordered monitor * is needed here. */ ts.unordered(recv_ctx, unordered_cb_); apply_monitor_.leave(ao); if (ts.is_toi()) { log_debug << "Done executing TO isolated action: " << ts.global_seqno(); st_.mark_safe(); } if (gu_likely(ts.local_seqno() != -1)) { // trx with local seqno -1 originates from IST (or other source not gcs) report_last_committed(safe_to_discard); } ts.set_exit_loop(exit_loop); } wsrep_status_t galera::ReplicatorSMM::send(TrxHandleMaster& trx, wsrep_trx_meta_t* meta) { assert(trx.locked()); if (state_() < S_JOINED) return WSREP_TRX_FAIL; // SR rollback const bool rollback(trx.flags() & TrxHandle::F_ROLLBACK); if (rollback) { assert(trx.state() == TrxHandle::S_ABORTING); assert((trx.flags() & TrxHandle::F_BEGIN) == 0); TrxHandleSlavePtr ts(TrxHandleSlave::New(true, slave_pool_), TrxHandleSlaveDeleter()); ts->set_global_seqno(0); trx.add_replicated(ts); } WriteSetNG::GatherVector actv; size_t act_size = trx.gather(actv); ssize_t rcode(0); do { const bool scheduled(!rollback); if (scheduled) { const ssize_t gcs_handle(gcs_.schedule()); if (gu_unlikely(gcs_handle < 0)) { log_debug << "gcs schedule " << strerror(-gcs_handle); rcode = gcs_handle; goto out; } trx.set_gcs_handle(gcs_handle); } trx.finalize(last_committed()); trx.unlock(); // On rollback fragment, we instruct sendv to use gcs_sm_grab() // to avoid the scenario where trx is BF aborted but can't send // ROLLBACK fragment due to flow control, which results in // deadlock. // Otherwise sendv call was scheduled above, and we instruct // the call to use regular gcs_sm_enter() const bool grab(rollback); rcode = gcs_.sendv(actv, act_size, GCS_ACT_WRITESET, scheduled, grab); GU_DBUG_SYNC_WAIT("after_send_sync"); trx.lock(); } // TODO: Break loop after some timeout while (rcode == -EAGAIN && (usleep(1000), true)); trx.set_gcs_handle(-1); out: if (rcode <= 0) { log_debug << "ReplicatorSMM::send failed: " << -rcode; } return (rcode > 0 ? WSREP_OK : WSREP_TRX_FAIL); } wsrep_status_t galera::ReplicatorSMM::replicate(TrxHandleMaster& trx, wsrep_trx_meta_t* meta, const wsrep_seq_cb_t* seq_cb) { assert(trx.locked()); assert(!(trx.flags() & TrxHandle::F_ROLLBACK)); assert(trx.state() == TrxHandle::S_EXECUTING || trx.state() == TrxHandle::S_MUST_ABORT); if (trx.version() >= 6) { /* By default append zero-level key */ galera::KeyData const k(trx.version()); gu_trace(trx.append_key(k)); } if (state_() < S_JOINED || trx.state() == TrxHandle::S_MUST_ABORT) { must_abort: if (trx.state() == TrxHandle::S_EXECUTING || trx.state() == TrxHandle::S_REPLICATING) TX_SET_STATE(trx, TrxHandle::S_MUST_ABORT); TX_SET_STATE(trx, TrxHandle::S_ABORTING); if (trx.ts() != 0) { assert(trx.ts()->state() == TrxHandle::S_COMMITTED); trx.reset_ts(); } return (st_.corrupt() ? WSREP_NODE_FAIL : WSREP_CONN_FAIL); } WriteSetNG::GatherVector actv; gcs_action act; act.type = GCS_ACT_WRITESET; #ifndef NDEBUG act.seqno_g = GCS_SEQNO_ILL; #endif act.buf = NULL; act.size = trx.gather(actv); TX_SET_STATE(trx, TrxHandle::S_REPLICATING); ssize_t rcode(-1); GU_DBUG_SYNC_WAIT("before_replicate_sync"); do { assert(act.seqno_g == GCS_SEQNO_ILL); const ssize_t gcs_handle(gcs_.schedule()); if (gu_unlikely(gcs_handle < 0)) { log_debug << "gcs schedule " << strerror(-gcs_handle); goto must_abort; } trx.set_gcs_handle(gcs_handle); trx.finalize(last_committed()); trx.unlock(); assert (act.buf == NULL); // just a sanity check rcode = gcs_.replv(actv, act, true, seq_cb); GU_DBUG_SYNC_WAIT("after_replicate_sync") trx.lock(); } while (rcode == -EAGAIN && trx.state() != TrxHandle::S_MUST_ABORT && (usleep(1000), true)); trx.set_gcs_handle(-1); if (rcode < 0) { if (rcode != -EINTR) { log_debug << "gcs_repl() failed with " << strerror(-rcode) << " for trx " << trx; } assert(rcode != -EINTR || trx.state() == TrxHandle::S_MUST_ABORT); assert(act.seqno_l == GCS_SEQNO_ILL && act.seqno_g == GCS_SEQNO_ILL); assert(NULL == act.buf); if (trx.state() != TrxHandle::S_MUST_ABORT) { TX_SET_STATE(trx, TrxHandle::S_MUST_ABORT); } goto must_abort; } assert(act.buf != NULL); assert(act.size == rcode); assert(act.seqno_l > 0); assert(act.seqno_g > 0); TrxHandleSlavePtr ts(TrxHandleSlave::New(true, slave_pool_), TrxHandleSlaveDeleter()); gu_trace(ts->unserialize(act)); ts->set_local(true); ts->update_stats(keys_count_, keys_bytes_, data_bytes_, unrd_bytes_); trx.add_replicated(ts); ++replicated_; replicated_bytes_ += rcode; assert(trx.source_id() == ts->source_id()); assert(trx.conn_id() == ts->conn_id()); assert(trx.trx_id() == ts->trx_id()); assert(ts->global_seqno() == act.seqno_g); assert(ts->last_seen_seqno() >= 0); assert(trx.ts() == ts); wsrep_status_t retval(WSREP_TRX_FAIL); // ROLLBACK event shortcut to avoid blocking in monitors or // getting BF aborted inside provider if (gu_unlikely(ts->flags() & TrxHandle::F_ROLLBACK)) { // ROLLBACK fragments should be replicated through ReplicatorSMM::send(), // assert here for debug builds to catch if this is not a case. assert(0); assert(ts->depends_seqno() > 0); // must be set at unserialization ts->cert_bypass(true); ts->mark_certified(); TX_SET_STATE(trx, TrxHandle::S_MUST_ABORT); TX_SET_STATE(trx, TrxHandle::S_ABORTING); pending_cert_queue_.push(ts); cancel_monitors_for_local(*ts); goto out; } if (gu_unlikely(trx.state() == TrxHandle::S_MUST_ABORT)) { retval = WSREP_BF_ABORT; // If the transaction was committing, it must replay. Otherwise // it was an intermediate fragment and we treat it as certification // failure. if (ts->flags() & TrxHandle::F_COMMIT) { TX_SET_STATE(trx, TrxHandle::S_MUST_REPLAY); } else { TX_SET_STATE(trx, TrxHandle::S_ABORTING); pending_cert_queue_.push(ts); cancel_monitors_for_local(*ts); retval = WSREP_TRX_FAIL; } } else { assert(trx.state() == TrxHandle::S_REPLICATING); retval = WSREP_OK; } out: assert(trx.state() != TrxHandle::S_MUST_ABORT); assert(ts->global_seqno() > 0); assert(ts->global_seqno() == act.seqno_g); if (meta != 0) // whatever the retval, we must update GTID in meta { meta->gtid.uuid = state_uuid_; meta->gtid.seqno = ts->global_seqno(); meta->depends_on = ts->depends_seqno(); } return retval; } wsrep_status_t galera::ReplicatorSMM::abort_trx(TrxHandleMaster& trx, wsrep_seqno_t bf_seqno, wsrep_seqno_t* victim_seqno) { assert(trx.local() == true); assert(trx.locked()); const TrxHandleSlavePtr ts(trx.ts()); if (ts) { log_debug << "aborting ts " << *ts; assert(ts->global_seqno() != WSREP_SEQNO_UNDEFINED); if (ts->global_seqno() < bf_seqno && (ts->flags() & TrxHandle::F_COMMIT)) { log_debug << "seqno " << bf_seqno << " trying to abort seqno " << ts->global_seqno(); *victim_seqno = ts->global_seqno(); return WSREP_NOT_ALLOWED; } } else { log_debug << "aborting trx " << trx; } wsrep_status_t retval(WSREP_OK); switch (trx.state()) { case TrxHandle::S_MUST_ABORT: case TrxHandle::S_ABORTING: case TrxHandle::S_MUST_REPLAY: // victim trx was already BF aborted or it failed certification retval = WSREP_NOT_ALLOWED; break; case TrxHandle::S_EXECUTING: TX_SET_STATE(trx, TrxHandle::S_MUST_ABORT); break; case TrxHandle::S_REPLICATING: { // @note: it is important to place set_state() into beginning of // every case, because state must be changed AFTER switch() and // BEFORE entering monitors or taking any other action. TX_SET_STATE(trx, TrxHandle::S_MUST_ABORT); int rc; if (trx.gcs_handle() > 0 && ((rc = gcs_.interrupt(trx.gcs_handle()))) != 0) { log_debug << "gcs_interrupt(): handle " << trx.gcs_handle() << " trx id " << trx.trx_id() << ": " << strerror(-rc); } break; } case TrxHandle::S_CERTIFYING: { // trx is waiting in local monitor assert(ts); assert(ts->global_seqno() > 0); log_debug << "aborting ts: " << *ts << "; BF seqno: " << bf_seqno << "; local position: " << local_monitor_.last_left(); TX_SET_STATE(trx, TrxHandle::S_MUST_ABORT); LocalOrder lo(*ts); local_monitor_.interrupt(lo); break; } case TrxHandle::S_APPLYING: { // trx is waiting in apply monitor assert(ts); assert(ts->global_seqno() > 0); log_debug << "aborting ts: " << *ts << "; BF seqno: " << bf_seqno << "; apply window: " << apply_monitor_.last_left() << " - " << apply_monitor_.last_entered(); TX_SET_STATE(trx, TrxHandle::S_MUST_ABORT); ApplyOrder ao(*ts); apply_monitor_.interrupt(ao); break; } case TrxHandle::S_COMMITTING: { // Trx is waiting in commit monitor assert(ts); assert(ts->global_seqno() > 0); log_debug << "aborting ts: " << *ts << "; BF seqno: " << bf_seqno << "; commit position: " << last_committed(); if (co_mode_ != CommitOrder::BYPASS) { CommitOrder co(*ts, co_mode_); bool const interrupted(commit_monitor_.interrupt(co)); if (interrupted || !(ts->flags() & TrxHandle::F_COMMIT)) { TX_SET_STATE(trx, TrxHandle::S_MUST_ABORT); } else { retval = WSREP_NOT_ALLOWED; } } break; } case TrxHandle::S_COMMITTED: assert(ts); assert(ts->global_seqno() > 0); if (ts->global_seqno() < bf_seqno && (ts->flags() & TrxHandle::F_COMMIT)) { retval = WSREP_NOT_ALLOWED; } else { retval = WSREP_OK; } break; case TrxHandle::S_ROLLING_BACK: log_error << "Attempt to enter commit monitor while holding " "locks in rollback by " << trx; // fallthrough default: log_warn << "invalid state " << trx.state() << " in abort_trx for trx" << trx; assert(0); } if (retval == WSREP_OK || retval == WSREP_NOT_ALLOWED) { *victim_seqno = (ts != 0 ? ts->global_seqno() : WSREP_SEQNO_UNDEFINED); } return retval; } wsrep_status_t galera::ReplicatorSMM::certify(TrxHandleMaster& trx, wsrep_trx_meta_t* meta) { assert(trx.state() == TrxHandle::S_REPLICATING); TrxHandleSlavePtr ts(trx.ts()); assert(ts->state() == TrxHandle::S_REPLICATING); // Rollback should complete with post_rollback assert((ts->flags() & TrxHandle::F_ROLLBACK) == 0); assert(ts->local_seqno() > 0); assert(ts->global_seqno() > 0); assert(ts->last_seen_seqno() >= 0); assert(ts->depends_seqno() >= -1); if (meta != 0) { assert(meta->gtid.uuid == state_uuid_); assert(meta->gtid.seqno == ts->global_seqno()); assert(meta->depends_on == ts->depends_seqno()); } // State should not be checked here: If trx has been replicated, // it has to be certified and potentially applied. #528 // if (state_() < S_JOINED) return WSREP_TRX_FAIL; wsrep_status_t retval(cert_and_catch(&trx, ts)); assert((ts->flags() & TrxHandle::F_ROLLBACK) == 0 || trx.state() == TrxHandle::S_ABORTING); if (gu_unlikely(retval != WSREP_OK)) { switch(retval) { case WSREP_BF_ABORT: assert(trx.state() == TrxHandle::S_MUST_REPLAY || !(ts->flags() & TrxHandle::F_COMMIT)); assert(ts->state() == TrxHandle::S_REPLICATING || ts->state() == TrxHandle::S_CERTIFYING); // apply monitor will be entered in due course during replay break; case WSREP_TRX_FAIL: /* committing fragment fails certification or non-committing BF'ed */ // If the ts was queued, the depends seqno cannot be trusted // as it may be modified concurrently. assert(ts->queued() || ts->is_dummy() || (ts->flags() & TrxHandle::F_COMMIT) == 0); assert(ts->state() == TrxHandle::S_CERTIFYING || ts->state() == TrxHandle::S_REPLICATING); if (ts->state() == TrxHandle::S_REPLICATING) TX_SET_STATE(*ts, TrxHandle::S_CERTIFYING); break; default: assert(0); } return retval; } else { if (meta) meta->depends_on = ts->depends_seqno(); if (enter_apply_monitor_for_local(trx, ts)) { TX_SET_STATE(*ts, TrxHandle::S_APPLYING); if (trx.state() == TrxHandle::S_MUST_ABORT) return WSREP_BF_ABORT; else return WSREP_OK; } else { return handle_apply_monitor_interrupted(trx, ts); } } } wsrep_status_t galera::ReplicatorSMM::replay_trx(TrxHandleMaster& trx, TrxHandleLock& lock, void* const trx_ctx) { TrxHandleSlavePtr tsp(trx.ts()); assert(tsp); TrxHandleSlave& ts(*tsp); assert(ts.global_seqno() > last_committed()); log_debug << "replay trx: " << trx << " ts: " << ts; if (trx.state() == TrxHandle::S_MUST_ABORT) { // BF aborted outside of provider. TX_SET_STATE(trx, TrxHandle::S_MUST_REPLAY); } assert(trx.state() == TrxHandle::S_MUST_REPLAY); assert(trx.trx_id() != static_cast(-1)); wsrep_status_t retval(WSREP_OK); // Note: We set submit NULL trx pointer below to avoid // interrupting replaying in any monitor during replay. switch (ts.state()) { case TrxHandle::S_REPLICATING: retval = cert_and_catch(&trx, tsp); assert(ts.state() == TrxHandle::S_CERTIFYING); if (retval != WSREP_OK) { assert(retval == WSREP_TRX_FAIL); assert(ts.is_dummy()); break; } // fall through case TrxHandle::S_CERTIFYING: { assert(ts.state() == TrxHandle::S_CERTIFYING); ApplyOrder ao(ts); assert(apply_monitor_.entered(ao) == false); gu_trace(apply_monitor_.enter(ao)); TX_SET_STATE(ts, TrxHandle::S_APPLYING); } // fall through case TrxHandle::S_APPLYING: // // Commit monitor will be entered from commit_order_enter_remote. // // fall through case TrxHandle::S_COMMITTING: ++local_replays_; // safety measure to make sure that all preceding trxs are // ordered for commit before replaying commit_monitor_.wait(ts.global_seqno() - 1); TX_SET_STATE(trx, TrxHandle::S_REPLAYING); try { // Only committing transactions should be replayed assert(ts.flags() & TrxHandle::F_COMMIT); wsrep_trx_meta_t meta = {{ state_uuid_, ts.global_seqno() }, { ts.source_id(), ts.trx_id(), ts.conn_id() }, ts.depends_seqno()}; /* failure to replay own trx is certainly a sign of inconsistency, * not trying to catch anything here */ assert(trx.owned()); bool unused(false); lock.unlock(); gu_trace(ts.apply(trx_ctx, apply_cb_, meta, unused)); lock.lock(); assert(false == unused); log_debug << "replayed " << ts.global_seqno(); assert(ts.state() == TrxHandle::S_COMMITTED); assert(trx.state() == TrxHandle::S_COMMITTED); } catch (gu::Exception& e) { on_inconsistency(); return WSREP_NODE_FAIL; } // apply, commit monitors are released in post commit return WSREP_OK; default: assert(0); gu_throw_fatal << "Invalid state in replay for trx " << trx; } log_debug << "replaying failed for trx " << trx; assert(trx.state() == TrxHandle::S_ABORTING); return retval; } static void dump_buf(std::ostream& os, const void* const buf, size_t const buf_len) { std::ios_base::fmtflags const saved_flags(os.flags()); char const saved_fill (os.fill('0')); os << std::oct; const char* const str(static_cast(buf)); for (size_t i(0); i < buf_len; ++i) { char const c(str[i]); if ('\0' == c) break; try { if (isprint(c) || isspace(c)) { os.put(c); } else { os << '\\' << std::setw(2) << int(c); } } catch (std::ios_base::failure& f) { log_warn << "Failed to dump " << i << "th byte: " << f.what(); break; } } os.flags(saved_flags); os.fill (saved_fill); } wsrep_status_t galera::ReplicatorSMM::handle_commit_interrupt(TrxHandleMaster& trx, const TrxHandleSlave& ts) { assert(trx.state() == TrxHandle::S_MUST_ABORT); if (ts.flags() & TrxHandle::F_COMMIT) { TX_SET_STATE(trx, TrxHandle::S_MUST_REPLAY); return WSREP_BF_ABORT; } else { TX_SET_STATE(trx, TrxHandle::S_ABORTING); return WSREP_TRX_FAIL; } } wsrep_status_t galera::ReplicatorSMM::commit_order_enter_local(TrxHandleMaster& trx) { assert(trx.local()); assert(trx.ts() && trx.ts()->global_seqno() > 0); assert(trx.locked()); assert(trx.state() == TrxHandle::S_APPLYING || trx.state() == TrxHandle::S_ABORTING || trx.state() == TrxHandle::S_REPLAYING); TrxHandleSlavePtr tsp(trx.ts()); TrxHandleSlave& ts(*tsp); if (trx.state() != TrxHandle::S_APPLYING) { // Transactions which are rolling back or replaying // may not have grabbed apply monitor so far. Do it // before proceeding. enter_apply_monitor_for_local_not_committing(trx, ts); } #ifndef NDEBUG { ApplyOrder ao(ts); assert(apply_monitor_.entered(ao)); } #endif // NDEBUG TrxHandle::State const next_state (trx.state() == TrxHandle::S_ABORTING ? TrxHandle::S_ROLLING_BACK : TrxHandle::S_COMMITTING); TX_SET_STATE(trx, next_state); if (co_mode_ == CommitOrder::BYPASS) { TX_SET_STATE(ts, TrxHandle::S_COMMITTING); return WSREP_OK; } CommitOrder co(ts, co_mode_); if (ts.state() < TrxHandle::S_COMMITTING) { assert(!commit_monitor_.entered(co)); } else { // was BF'ed after having entered commit monitor. This may happen // for SR fragment. assert(commit_monitor_.entered(co)); return WSREP_OK; } try { trx.unlock(); GU_DBUG_SYNC_WAIT("before_local_commit_monitor_enter"); gu_trace(commit_monitor_.enter(co)); assert(commit_monitor_.entered(co)); trx.lock(); TX_SET_STATE(ts, TrxHandle::S_COMMITTING); /* non-committing fragments may be interrupted after having entered * commit_monitor_ */ if (0 == (ts.flags() & TrxHandle::F_COMMIT) && trx.state() == TrxHandle::S_MUST_ABORT) return handle_commit_interrupt(trx, ts); assert(trx.state() == TrxHandle::S_COMMITTING || trx.state() == TrxHandle::S_ROLLING_BACK); } catch (gu::Exception& e) { assert(!commit_monitor_.entered(co)); assert(next_state != TrxHandle::S_ROLLING_BACK); trx.lock(); if (e.get_errno() == EINTR) { return handle_commit_interrupt(trx, ts); } else throw; } assert(ts.global_seqno() > last_committed()); assert(trx.locked()); assert(trx.state() == TrxHandle::S_COMMITTING || trx.state() == TrxHandle::S_ROLLING_BACK); return WSREP_OK; } wsrep_status_t galera::ReplicatorSMM::commit_order_enter_remote(TrxHandleSlave& trx) { assert(trx.global_seqno() > 0); assert(trx.state() == TrxHandle::S_APPLYING || trx.state() == TrxHandle::S_ABORTING); #ifndef NDEBUG if (trx.state() == TrxHandle::S_REPLAYING) { assert(trx.local()); assert((trx.flags() & TrxHandle::F_ROLLBACK) == 0); ApplyOrder ao(trx); assert(apply_monitor_.entered(ao)); } #endif /* NDEBUG */ CommitOrder co(trx, co_mode_); assert(!commit_monitor_.entered(co)); if (gu_likely(co_mode_ != CommitOrder::BYPASS)) { gu_trace(commit_monitor_.enter(co)); } TX_SET_STATE(trx, TrxHandle::S_COMMITTING); return WSREP_OK; } void galera::ReplicatorSMM::process_apply_error(TrxHandleSlave& trx, const wsrep_buf_t& error) { gu::GTID const gtid(state_uuid_, trx.global_seqno()); int res; if (trx.local_seqno() != -1 || trx.nbo_end()) { /* this must be done IN ORDER to avoid multiple elections, hence * anything else but LOCAL_OOOC and NO_OOOC is potentially broken */ res = gcs_.vote(gtid, -1, error.ptr, error.len); } else res = 2; if (res != 0) { std::ostringstream os; switch (res) { case 2: os << "Failed on preordered " << gtid << ": inconsistency."; break; case 1: os << "Inconsistent by consensus on " << gtid; break; default: os << "Could not reach consensus on " << gtid << " (rcode: " << res << "), assuming inconsistency."; } galera::ApplyException ae(os.str(), NULL, error.ptr, error.len); GU_TRACE(ae); throw ae; } else { /* mark action as invalid (skip seqno) and return normally */ gcache_.seqno_skip(trx.action().first, trx.global_seqno(), GCS_ACT_WRITESET); } } wsrep_status_t galera::ReplicatorSMM::handle_apply_error(TrxHandleSlave& ts, const wsrep_buf_t& error, const std::string& custom_msg) { assert(error.len > 0); std::ostringstream os; os << custom_msg << ts.global_seqno() << ", error: "; dump_buf(os, error.ptr, error.len); log_debug << "handle_apply_error(): " << os.str(); try { if (!st_.corrupt()) gu_trace(process_apply_error(ts, error)); return WSREP_OK; } catch (ApplyException& e) { log_error << "Inconsistency detected: " << e.what(); on_inconsistency(); } catch (gu::Exception& e) { log_error << "Unexpected exception: " << e.what(); assert(0); abort(); } catch (...) { log_error << "Unknown exception"; assert(0); abort(); } return WSREP_NODE_FAIL; } wsrep_status_t galera::ReplicatorSMM::commit_order_leave(TrxHandleSlave& ts, const wsrep_buf_t* const error) { assert(ts.state() == TrxHandle::S_COMMITTING); #ifndef NDEBUG { CommitOrder co(ts, co_mode_); assert(co_mode_ != CommitOrder::BYPASS || commit_monitor_.entered(co)); } #endif wsrep_status_t retval(WSREP_OK); if (gu_unlikely(error != NULL && error->ptr != NULL)) { retval = handle_apply_error(ts, *error, "Failed to apply writeset "); } if (gu_likely(co_mode_ != CommitOrder::BYPASS)) { CommitOrder co(ts, co_mode_); commit_monitor_.leave(co); } TX_SET_STATE(ts, TrxHandle::S_COMMITTED); /* master state will be set upon release */ return retval; } wsrep_status_t galera::ReplicatorSMM::release_commit(TrxHandleMaster& trx) { TrxHandleSlavePtr tsp(trx.ts()); assert(tsp); TrxHandleSlave& ts(*tsp); #ifndef NDEBUG { CommitOrder co(ts, co_mode_); assert(co_mode_ == CommitOrder::BYPASS || commit_monitor_.entered(co) == false); } #endif log_debug << "release_commit() for trx: " << trx << " ts: " << ts; assert((ts.flags() & TrxHandle::F_ROLLBACK) == 0); assert(ts.local_seqno() > 0 && ts.global_seqno() > 0); assert(ts.state() == TrxHandle::S_COMMITTED); // Streaming transaction may enter here in aborting state if the // BF abort happens during fragment commit ordering. Otherwise // should always be committed. assert(trx.state() == TrxHandle::S_COMMITTED || (trx.state() == TrxHandle::S_ABORTING && (ts.flags() & TrxHandle::F_COMMIT) == 0)); assert(!ts.is_committed()); wsrep_seqno_t const safe_to_discard(cert_.set_trx_committed(ts)); ApplyOrder ao(ts); apply_monitor_.leave(ao); if ((ts.flags() & TrxHandle::F_COMMIT) == 0 && trx.state() == TrxHandle::S_COMMITTED) { // continue streaming TX_SET_STATE(trx, TrxHandle::S_EXECUTING); } trx.reset_ts(); ++local_commits_; report_last_committed(safe_to_discard); return WSREP_OK; } wsrep_status_t galera::ReplicatorSMM::release_rollback(TrxHandleMaster& trx) { assert(trx.locked()); if (trx.state() == TrxHandle::S_MUST_ABORT) // BF abort before replicaiton TX_SET_STATE(trx, TrxHandle::S_ABORTING); if (trx.state() == TrxHandle::S_ABORTING || trx.state() == TrxHandle::S_EXECUTING) TX_SET_STATE(trx, TrxHandle::S_ROLLED_BACK); assert(trx.state() == TrxHandle::S_ROLLED_BACK); TrxHandleSlavePtr tsp(trx.ts()); if (tsp) { TrxHandleSlave& ts(*tsp); log_debug << "release_rollback() trx: " << trx << ", ts: " << ts; assert(ts.global_seqno() > 0); if (ts.global_seqno() > 0) { ApplyOrder ao(ts.global_seqno(), 0, ts.local()); // Enter and leave monitors if they were not entered/canceled // already. if (ts.state() < TrxHandle::S_COMMITTED) { if (ts.state() < TrxHandle::S_CERTIFYING) { TX_SET_STATE(ts, TrxHandle::S_CERTIFYING); } if (ts.state() < TrxHandle::S_APPLYING) { apply_monitor_.enter(ao); TX_SET_STATE(ts, TrxHandle::S_APPLYING); } CommitOrder co(ts, co_mode_); if (ts.state() < TrxHandle::S_COMMITTING) { commit_monitor_.enter(co); TX_SET_STATE(ts, TrxHandle::S_COMMITTING); } commit_monitor_.leave(co); assert(co_mode_ != CommitOrder::NO_OOOC || commit_monitor_.last_left() >= ts.global_seqno()); TX_SET_STATE(ts, TrxHandle::S_COMMITTED); } /* Queued transactions will be set committed in the queue */ wsrep_seqno_t const safe_to_discard (ts.queued() ? WSREP_SEQNO_UNDEFINED : cert_.set_trx_committed(ts)); apply_monitor_.leave(ao); if (safe_to_discard != WSREP_SEQNO_UNDEFINED) report_last_committed(safe_to_discard); } } else { log_debug << "release_rollback() trx: " << trx << ", ts: nil"; } // Trx was either rolled back by user or via certification failure, // last committed report not needed since cert index state didn't change. // report_last_committed(); trx.reset_ts(); ++local_rollbacks_; return WSREP_OK; } wsrep_status_t galera::ReplicatorSMM::sync_wait(wsrep_gtid_t* upto, int tout, wsrep_gtid_t* gtid) { gu::GTID wait_gtid; gu::datetime::Date wait_until(gu::datetime::Date::calendar() + ((tout == -1) ? gu::datetime::Period(causal_read_timeout_) : gu::datetime::Period(tout * gu::datetime::Sec))); if (upto == 0) { try { gcs_.caused(wait_gtid, wait_until); } catch (gu::Exception& e) { log_debug << "gcs_caused() returned " << -e.get_errno() << " (" << strerror(e.get_errno()) << ")"; return WSREP_TRX_FAIL; } } else { wait_gtid.set(upto->uuid, upto->seqno); } try { // @note: Using timed wait for monitor is currently a hack // to avoid deadlock resulting from race between monitor wait // and drain during configuration change. Instead of this, // monitor should have proper mechanism to interrupt waiters // at monitor drain and disallowing further waits until // configuration change related operations (SST etc) have been // finished. // Note: Since wsrep API 26 application may request release of // commit monitor before the commit actually happens (commit // may have been ordered/queued on application side for later // processing). Therefore we now rely on apply_monitor on sync // wait. This is sufficient since apply_monitor is always released // only after the whole transaction is over. apply_monitor_.wait(wait_gtid, wait_until); if (gtid != 0) { (void)last_committed_id(gtid); } ++causal_reads_; return WSREP_OK; } catch (gu::NotFound& e) { log_debug << "monitor wait failed for sync_wait: UUID mismatch"; return WSREP_TRX_MISSING; } catch (gu::Exception& e) { log_debug << "monitor wait failed for sync_wait: " << e.what(); return WSREP_TRX_FAIL; } } wsrep_status_t galera::ReplicatorSMM::last_committed_id(wsrep_gtid_t* gtid) const { // Note that we need to use apply monitor to determine last committed // here. Due to group commit implementation, the commit monitor may // be released before the commit has finished and the changes // made by the transaction have become visible. Therefore we rely // on apply monitor since it remains grabbed until the whole // commit is over. apply_monitor_.last_left_gtid(*gtid); return WSREP_OK; } wsrep_status_t galera::ReplicatorSMM::wait_nbo_end(TrxHandleMaster* trx, wsrep_trx_meta_t* meta) { gu::shared_ptr::type nbo_ctx(cert_.nbo_ctx(meta->gtid.seqno)); // Send end message trx->set_state(TrxHandle::S_REPLICATING); WriteSetNG::GatherVector actv; size_t const actv_size( trx->write_set_out().gather(trx->source_id(), trx->conn_id(), trx->trx_id(), actv)); resend: wsrep_seqno_t lc(last_committed()); if (lc == WSREP_SEQNO_UNDEFINED) { // Provider has been closed return WSREP_NODE_FAIL; } trx->finalize(lc); trx->unlock(); int err(gcs_.sendv(actv, actv_size, GCS_ACT_WRITESET, false, false)); trx->lock(); if (err == -EAGAIN || err == -ENOTCONN || err == -EINTR) { // Send was either interrupted due to states excahnge (EAGAIN), // due to non-prim (ENOTCONN) or due to timeout in send monitor // (EINTR). return WSREP_CONN_FAIL; } else if (err < 0) { log_error << "Failed to send NBO-end: " << err << ": " << gcs_error_str(-err); return WSREP_NODE_FAIL; } TrxHandleSlavePtr end_ts; while ((end_ts = nbo_ctx->wait_ts()) == 0) { if (closing_ || state_() == S_CLOSED) { log_error << "Closing during nonblocking operation. " "Node will be left in inconsistent state and must be " "re-initialized either by full SST or from backup."; return WSREP_FATAL; } if (nbo_ctx->aborted()) { log_debug << "NBO wait aborted, retrying send"; // Wait was aborted by view change, resend message goto resend; } } assert(end_ts->ends_nbo() != WSREP_SEQNO_UNDEFINED); trx->add_replicated(end_ts); meta->gtid.uuid = state_uuid_; meta->gtid.seqno = end_ts->global_seqno(); meta->depends_on = end_ts->depends_seqno(); ApplyOrder ao(*end_ts); apply_monitor_.enter(ao); CommitOrder co(*end_ts, co_mode_); if (co_mode_ != CommitOrder::BYPASS) { commit_monitor_.enter(co); } end_ts->set_state(TrxHandle::S_APPLYING); end_ts->set_state(TrxHandle::S_COMMITTING); trx->set_state(TrxHandle::S_CERTIFYING); trx->set_state(TrxHandle::S_APPLYING); trx->set_state(TrxHandle::S_COMMITTING); // Unref cert_.erase_nbo_ctx(end_ts->ends_nbo()); return WSREP_OK; } wsrep_status_t galera::ReplicatorSMM::to_isolation_begin(TrxHandleMaster& trx, wsrep_trx_meta_t* meta) { assert(trx.locked()); if (trx.nbo_end()) { return wait_nbo_end(&trx, meta); } TrxHandleSlavePtr ts_ptr(trx.ts()); TrxHandleSlave& ts(*ts_ptr); if (meta != 0) { assert(meta->gtid.seqno > 0); assert(meta->gtid.seqno == ts.global_seqno()); assert(meta->depends_on == ts.depends_seqno()); } assert(trx.state() == TrxHandle::S_REPLICATING); assert(trx.trx_id() == static_cast(-1)); assert(ts.local_seqno() > -1 && ts.global_seqno() > -1); assert(ts.global_seqno() > last_committed()); CommitOrder co(ts, co_mode_); wsrep_status_t const retval(cert_and_catch(&trx, ts_ptr)); ApplyOrder ao(ts); gu_trace(apply_monitor_.enter(ao)); switch (retval) { case WSREP_OK: { TX_SET_STATE(trx, TrxHandle::S_APPLYING); TX_SET_STATE(ts, TrxHandle::S_APPLYING); TX_SET_STATE(trx, TrxHandle::S_COMMITTING); TX_SET_STATE(ts, TrxHandle::S_COMMITTING); break; } case WSREP_TRX_FAIL: break; default: assert(0); gu_throw_fatal << "unrecognized retval " << retval << " for to isolation certification for " << ts; break; } if (co_mode_ != CommitOrder::BYPASS) try { commit_monitor_.enter(co); if (ts.state() == TrxHandle::S_COMMITTING) { log_debug << "Executing TO isolated action: " << ts; st_.mark_unsafe(); } else { log_debug << "Grabbed TO for failed isolated action: " << ts; assert(trx.state() == TrxHandle::S_ABORTING); } } catch (...) { gu_throw_fatal << "unable to enter commit monitor: " << ts; } return retval; } wsrep_status_t galera::ReplicatorSMM::to_isolation_end(TrxHandleMaster& trx, const wsrep_buf_t* const err) { TrxHandleSlavePtr ts_ptr(trx.ts()); TrxHandleSlave& ts(*ts_ptr); log_debug << "Done executing TO isolated action: " << ts; assert(trx.state() == TrxHandle::S_COMMITTING || trx.state() == TrxHandle::S_ABORTING); assert(ts.state() == TrxHandle::S_COMMITTING || ts.state() == TrxHandle::S_CERTIFYING); wsrep_status_t ret(WSREP_OK); if (NULL != err && NULL != err->ptr) { ret = handle_apply_error(ts, *err, "Failed to execute TOI action "); } CommitOrder co(ts, co_mode_); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.leave(co); wsrep_seqno_t const safe_to_discard(cert_.set_trx_committed(ts)); ApplyOrder ao(ts); apply_monitor_.leave(ao); if (ts.state() == TrxHandle::S_COMMITTING) { assert(trx.state() == TrxHandle::S_COMMITTING); TX_SET_STATE(trx, TrxHandle::S_COMMITTED); TX_SET_STATE(ts, TrxHandle::S_COMMITTED); if (trx.nbo_start() == false) st_.mark_safe(); } else { assert(trx.state() == TrxHandle::S_ABORTING); assert(ts.state() == TrxHandle::S_CERTIFYING); TX_SET_STATE(trx, TrxHandle::S_ROLLED_BACK); TX_SET_STATE(ts, TrxHandle::S_APPLYING); TX_SET_STATE(ts, TrxHandle::S_COMMITTING); TX_SET_STATE(ts, TrxHandle::S_COMMITTED); } report_last_committed(safe_to_discard); return ret; } namespace galera { static WriteSetOut* writeset_from_handle (wsrep_po_handle_t& handle, const TrxHandleMaster::Params& trx_params) { WriteSetOut* ret = static_cast(handle.opaque); if (NULL == ret) { try { ret = new WriteSetOut( // gu::String<256>(trx_params.working_dir_) << '/' << &handle, trx_params.working_dir_, wsrep_trx_id_t(&handle), /* key format is not essential since we're not adding keys */ KeySet::version(trx_params.key_format_), NULL, 0, 0, trx_params.record_set_ver_, WriteSetNG::MAX_VERSION, DataSet::MAX_VERSION, DataSet::MAX_VERSION, trx_params.max_write_set_size_); handle.opaque = ret; } catch (std::bad_alloc& ba) { gu_throw_error(ENOMEM) << "Could not create WriteSetOut"; } } return ret; } } /* namespace galera */ wsrep_status_t galera::ReplicatorSMM::preordered_collect(wsrep_po_handle_t& handle, const struct wsrep_buf* const data, size_t const count, bool const copy) { WriteSetOut* const ws(writeset_from_handle(handle, trx_params_)); for (size_t i(0); i < count; ++i) { ws->append_data(data[i].ptr, data[i].len, copy); } return WSREP_OK; } wsrep_status_t galera::ReplicatorSMM::preordered_commit(wsrep_po_handle_t& handle, const wsrep_uuid_t& source, uint64_t const flags, int const pa_range, bool const commit) { WriteSetOut* const ws(writeset_from_handle(handle, trx_params_)); if (gu_likely(true == commit)) { assert(source != WSREP_UUID_UNDEFINED); ws->set_flags (WriteSetNG::wsrep_flags_to_ws_flags(flags) | WriteSetNG::F_PREORDERED); /* by loooking at trx_id we should be able to detect gaps / lost events * (however resending is not implemented yet). Something like * * wsrep_trx_id_t const trx_id(cert_.append_preordered(source, ws)); * * begs to be here. */ wsrep_trx_id_t const trx_id(preordered_id_.add_and_fetch(1)); WriteSetNG::GatherVector actv; size_t const actv_size(ws->gather(source, 0, trx_id, actv)); ws->finalize_preordered(pa_range); // also adds checksum int rcode; do { rcode = gcs_.sendv(actv, actv_size, GCS_ACT_WRITESET, false, false); } while (rcode == -EAGAIN && (usleep(1000), true)); if (rcode < 0) gu_throw_error(-rcode) << "Replication of preordered writeset failed: " << gcs_error_str(-rcode); } delete ws; // cleanup regardless of commit flag handle.opaque = NULL; return WSREP_OK; } wsrep_status_t galera::ReplicatorSMM::sst_sent(const wsrep_gtid_t& state_id, int rcode) { assert (rcode <= 0); assert (rcode == 0 || state_id.seqno == WSREP_SEQNO_UNDEFINED); assert (rcode != 0 || state_id.seqno >= 0); if (state_() != S_DONOR) { log_error << "sst sent called when not SST donor, state " << state_(); return WSREP_CONN_FAIL; } if (state_id.uuid != state_uuid_ && rcode >= 0) { // state we have sent no longer corresponds to the current group state // mark an error rcode = -EREMCHG; } try { if (rcode == 0) gcs_.join(gu::GTID(state_id.uuid, state_id.seqno), rcode); else /* stamp error message with the current state */ gcs_.join(gu::GTID(state_uuid_, commit_monitor_.last_left()), rcode); return WSREP_OK; } catch (gu::Exception& e) { log_error << "failed to recover from DONOR state: " << e.what(); return WSREP_CONN_FAIL; } } // Checks if the seqno has been assgined for the gcache buffer. // If yes, discard the old and use the one assigned in IST. // This is required to make the correct gcache buffer associated // with certification index entries. galera::TrxHandleSlavePtr galera::ReplicatorSMM::get_real_ts_with_gcache_buffer( const TrxHandleSlavePtr& ts) { try { ssize_t size; const void* buf(gcache_.seqno_get_ptr(ts->global_seqno(), size)); // GCache seqno_get_ptr() did not throw, so there was a matching // entry in GCache. Construct a new TrxHandleSlavePtr from // existing gcache buffer and discard the old one. TrxHandleSlavePtr ret(TrxHandleSlave::New(false, slave_pool_), TrxHandleSlaveDeleter()); if (size > 0) { gu_trace(ret->unserialize( gcs_action{ts->global_seqno(), WSREP_SEQNO_UNDEFINED, buf, int32_t(size), GCS_ACT_WRITESET})); ret->set_local(false); assert(ret->global_seqno() == ts->global_seqno()); assert(ret->depends_seqno() >= 0 || ts->nbo_end()); assert(ret->action().first && ret->action().second); ret->verify_checksum(); } else { ret->set_global_seqno(ts->global_seqno()); ret->mark_dummy_with_action(buf); } // The bufs should never match as the seqno should not have been // yet assigned to buf on this codepath. assert(ts->action().first != buf); // Free duplicate buffer. if (ts->action().first != buf) { gcache_.free(const_cast(ts->action().first)); } return ret; } catch (const gu::NotFound&) { // Seqno was not assigned to this buffer, so it was not part of // IST processing and was allocated by GCS. gcache_.seqno_assign(ts->action().first, ts->global_seqno(), GCS_ACT_WRITESET, false); return ts; } } void galera::ReplicatorSMM::handle_trx_overlapping_ist( const TrxHandleSlavePtr& ts) { // Out of order processing. IST has already applied the // trx. assert (ts->global_seqno() <= apply_monitor_.last_left()); assert(not ts->local()); // Use local seqno from original ts for local monitor. LocalOrder lo(ts->local_seqno(), ts.get()); // Get real_ts pointing to GCache buffer which will not be discarded // if there is overlap. Do not try to access ts after this line. auto real_ts(get_real_ts_with_gcache_buffer(ts)); local_monitor_.enter(lo); // If global seqno is higher than certification position, this // trx was not part of the preload ad must be appended to // certification index. if (real_ts->global_seqno() > cert_.position()) { // We don't care about the result, just populate the index // and mark trx committed in certification. // see skip_prim_conf_change() for analogous logic (void)cert_.append_trx(real_ts); report_last_committed(cert_.set_trx_committed(*real_ts)); } local_monitor_.leave(lo); } void galera::ReplicatorSMM::process_trx(void* recv_ctx, const TrxHandleSlavePtr& ts_ptr) { assert(recv_ctx != 0); assert(ts_ptr != 0); TrxHandleSlave& ts(*ts_ptr); assert(ts.local_seqno() > 0); assert(ts.global_seqno() > 0); assert(ts.last_seen_seqno() >= 0); assert(ts.depends_seqno() == -1 || ts.version() >= 4); assert(ts.state() == TrxHandle::S_REPLICATING); // SST thread drains monitors after IST, so this should be // safe way to check if the ts was contained in IST. if (ts.global_seqno() <= apply_monitor_.last_left()) { handle_trx_overlapping_ist(ts_ptr); return; } wsrep_status_t const retval(cert_and_catch(0, ts_ptr)); switch (retval) { case WSREP_TRX_FAIL: assert(ts.is_dummy()); /* fall through to apply_trx() */ case WSREP_OK: try { if (ts.nbo_end() == true) { // NBO-end events are for internal operation only, not to be // consumed by application. If the NBO end happens with // different seqno than the current event's global seqno, // release monitors. In other case monitors will be grabbed // by local NBO handler threads. if (ts.ends_nbo() == WSREP_SEQNO_UNDEFINED) { assert(WSREP_OK != retval); assert(ts.is_dummy()); } else { assert(WSREP_OK == retval); assert(ts.ends_nbo() > 0); // Signal NBO waiter here after leaving local ordering // critical section. gu::shared_ptr::type nbo_ctx( cert_.nbo_ctx(ts.ends_nbo())); assert(nbo_ctx != 0); nbo_ctx->set_ts(ts_ptr); break; } } gu_trace(apply_trx(recv_ctx, ts)); } catch (std::exception& e) { log_fatal << "Failed to apply trx: " << ts; log_fatal << e.what(); log_fatal << "Node consistency compromized, leaving cluster..."; mark_corrupt_and_close(); assert(0); // this is an unexpected exception // keep processing events from the queue until provider is closed } break; default: // this should not happen for remote actions gu_throw_error(EINVAL) << "unrecognized retval for remote trx certification: " << retval << " trx: " << ts; } } void galera::ReplicatorSMM::process_commit_cut(wsrep_seqno_t const seq, wsrep_seqno_t const seqno_l) { assert(seq > 0); assert(seqno_l > 0); LocalOrder lo(seqno_l); gu_trace(local_monitor_.enter(lo)); process_pending_queue(seqno_l); if (seq >= cc_seqno_) /* Refs #782. workaround for * assert(seqno >= seqno_released_) in gcache. */ { if (state_() != S_SYNCED) { // make sure that all preceding actions committed // when node is SYNCED seq can't exceed last_committed() apply_monitor_.wait(seq); } assert(seq <= last_committed()); cert_.purge_trxs_upto(seq, true); } local_monitor_.leave(lo); log_debug << "Got commit cut from GCS: " << seq; } /* NB: the only use for this method is in cancel_seqnos() below */ void galera::ReplicatorSMM::cancel_seqno(wsrep_seqno_t const seqno) { assert(seqno > 0); ApplyOrder ao(seqno, seqno - 1); apply_monitor_.self_cancel(ao); if (co_mode_ != CommitOrder::BYPASS) { CommitOrder co(seqno, co_mode_); commit_monitor_.self_cancel(co); } } /* NB: the only use for this method is to dismiss the slave queue * in corrupt state */ void galera::ReplicatorSMM::cancel_seqnos(wsrep_seqno_t const seqno_l, wsrep_seqno_t const seqno_g) { if (seqno_l > 0) { LocalOrder lo(seqno_l); local_monitor_.self_cancel(lo); } if (seqno_g > 0) cancel_seqno(seqno_g); } void galera::ReplicatorSMM::drain_monitors(wsrep_seqno_t const upto) { apply_monitor_.drain(upto); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.drain(upto); } void galera::ReplicatorSMM::process_vote(wsrep_seqno_t const seqno_g, wsrep_seqno_t const seqno_l, int64_t const code) { assert(seqno_g > 0); assert(seqno_l > 0); std::ostringstream msg; LocalOrder lo(seqno_l); gu_trace(local_monitor_.enter(lo)); gu::GTID const gtid(state_uuid_, seqno_g); if (code > 0) /* vote request */ { assert(GCS_VOTE_REQUEST == code); log_info << "Got vote request for seqno " << gtid; //remove /* make sure WS was either successfully applied or already voted */ if (last_committed() < seqno_g) drain_monitors(seqno_g); if (st_.corrupt()) goto out; int const ret(gcs_.vote(gtid, 0, NULL, 0)); switch (ret) { case 0: /* majority agrees */ log_info << "Vote 0 (success) on " << gtid << " is consistent with group. Continue."; goto out; case -EALREADY: /* already voted */ log_info << gtid << " already voted on. Continue."; goto out; case 1: /* majority disagrees */ msg << "Vote 0 (success) on " << gtid << " is inconsistent with group. Leaving cluster."; goto fail; default: /* general error */ assert(ret < 0); msg << "Failed to vote on request for " << gtid << ": " << -ret << " (" << gcs_error_str(-ret) << "). " "Assuming inconsistency"; goto fail; } } else if (code < 0) { msg << "Got negative vote on successfully applied " << gtid; fail: log_error << msg.str(); on_inconsistency(); } else { /* seems we are in majority */ } out: local_monitor_.leave(lo); } void galera::ReplicatorSMM::set_initial_position(const wsrep_uuid_t& uuid, wsrep_seqno_t const seqno) { update_state_uuid(uuid); apply_monitor_.set_initial_position(uuid, seqno); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.set_initial_position(uuid, seqno); } std::tuple galera::get_trx_protocol_versions(int proto_ver) { enum gu::RecordSet::Version record_set_ver(gu::RecordSet::EMPTY); int trx_ver(-1); switch (proto_ver) { case 1: trx_ver = 1; record_set_ver = gu::RecordSet::VER1; break; case 2: trx_ver = 1; record_set_ver = gu::RecordSet::VER1; break; case 3: case 4: trx_ver = 2; record_set_ver = gu::RecordSet::VER1; break; case 5: trx_ver = 3; record_set_ver = gu::RecordSet::VER1; break; case 6: trx_ver = 3; record_set_ver = gu::RecordSet::VER1; break; case 7: // Protocol upgrade to handle IST SSL backwards compatibility, // no effect to TRX or STR protocols. trx_ver = 3; record_set_ver = gu::RecordSet::VER1; break; case 8: // Protocol upgrade to enforce 8-byte alignment in writesets and CCs trx_ver = 3; record_set_ver = gu::RecordSet::VER2; break; case 9: // Protocol upgrade to enable support for semi-shared key type. trx_ver = 4; record_set_ver = gu::RecordSet::VER2; break; case 10: // Protocol upgrade to enable support for: trx_ver = 5; // PA range preset in the writeset, // WSREP_KEY_UPDATE support (API v26) record_set_ver = gu::RecordSet::VER2; break; case 11: // Protocol upgrade to enable support for: trx_ver = 6; // zero-level key in the writeset record_set_ver = gu::RecordSet::VER2; break; default: gu_throw_error(EPROTO) << "Configuration change resulted in an unsupported protocol " "version: " << proto_ver << ". Can't continue."; }; return std::make_tuple(trx_ver, record_set_ver); } void galera::ReplicatorSMM::establish_protocol_versions (int proto_ver) { try { const auto trx_versions(get_trx_protocol_versions(proto_ver)); trx_params_.version_ = std::get<0>(trx_versions); trx_params_.record_set_ver_ = std::get<1>(trx_versions); protocol_version_ = proto_ver; log_info << "REPL Protocols: " << protocol_version_ << " (" << trx_params_.version_ << ")"; } catch (const gu::Exception& e) { log_fatal << "Caught exception: " << e.what(); abort(); } } void galera::ReplicatorSMM::record_cc_seqnos(wsrep_seqno_t cc_seqno, const char* source) { cc_seqno_ = cc_seqno; cc_lowest_trx_seqno_ = cert_.lowest_trx_seqno(); log_info << "Lowest cert index boundary for CC from " << source << ": " << cc_lowest_trx_seqno_;; log_info << "Min available from gcache for CC from " << source << ": " << gcache_.seqno_min(); // Lowest TRX must not have been released from gcache at this // point. assert(cc_lowest_trx_seqno_ >= gcache_.seqno_min()); } void galera::ReplicatorSMM::update_incoming_list(const wsrep_view_info_t& view) { static char const separator(','); ssize_t new_size(0); if (view.memb_num > 0) { new_size += view.memb_num - 1; // separators for (int i = 0; i < view.memb_num; ++i) { new_size += strlen(view.members[i].incoming); } } gu::Lock lock(incoming_mutex_); incoming_list_.clear(); incoming_list_.resize(new_size); if (new_size <= 0) return; incoming_list_ = view.members[0].incoming; for (int i = 1; i < view.memb_num; ++i) { incoming_list_ += separator; incoming_list_ += view.members[i].incoming; } } static galera::Replicator::State state2repl(gcs_node_state const my_state, int const my_idx) { switch (my_state) { case GCS_NODE_STATE_NON_PRIM: case GCS_NODE_STATE_PRIM: return galera::Replicator::S_CONNECTED; case GCS_NODE_STATE_JOINER: return galera::Replicator::S_JOINING; case GCS_NODE_STATE_JOINED: return galera::Replicator::S_JOINED; case GCS_NODE_STATE_SYNCED: return galera::Replicator::S_SYNCED; case GCS_NODE_STATE_DONOR: return galera::Replicator::S_DONOR; case GCS_NODE_STATE_MAX: assert(0); } gu_throw_fatal << "unhandled gcs state: " << my_state; GU_DEBUG_NORETURN; } void galera::ReplicatorSMM::submit_view_info(void* recv_ctx, const wsrep_view_info_t* view_info) { wsrep_cb_status_t const rcode (view_cb_(app_ctx_, recv_ctx, view_info, 0, 0)); if (WSREP_CB_SUCCESS != rcode) { gu_throw_fatal << "View callback failed. " "This is unrecoverable, restart required."; } } void galera::ReplicatorSMM::process_conf_change(void* recv_ctx, const struct gcs_action& cc) { assert(cc.seqno_l > 0); // Must not be from IST gcs_act_cchange const conf(cc.buf, cc.size); LocalOrder lo(cc.seqno_l); local_monitor_.enter(lo); process_pending_queue(cc.seqno_l); if (conf.conf_id < 0) { process_non_prim_conf_change(recv_ctx, conf, cc.seqno_g); gcache_.free(const_cast(cc.buf)); } else { process_prim_conf_change(recv_ctx, conf, cc.seqno_g, const_cast(cc.buf)); } resume_recv(); local_monitor_.leave(lo); if (conf.memb.size() == 0) { log_debug << "Received SELF-LEAVE. Connection closed."; assert(conf.conf_id < 0 && cc.seqno_g < 0); gu::Lock lock(closing_mutex_); shift_to_CLOSED(); } } void galera::ReplicatorSMM::drain_monitors_for_local_conf_change() { wsrep_seqno_t const upto(cert_.position()); assert(upto >= last_committed()); if (upto >= last_committed()) { log_debug << "Drain monitors from " << last_committed() << " up to " << upto; gu_trace(drain_monitors(upto)); } else { log_warn << "Cert position " << upto << " less than last committed " << last_committed(); } } void galera::ReplicatorSMM::process_non_prim_conf_change( void* recv_ctx, const gcs_act_cchange& conf, int const my_index) { assert(conf.conf_id == WSREP_SEQNO_UNDEFINED); /* ignore outdated non-prim configuration change */ if (conf.uuid == state_uuid_ && conf.seqno < sst_seqno_) return; wsrep_uuid_t new_uuid(uuid_); wsrep_view_info_t* const view_info (galera_view_info_create(conf, capabilities(conf.repl_proto_ver), my_index, new_uuid)); // Non-prim should not change UUID assert(uuid_ == WSREP_UUID_UNDEFINED || new_uuid == uuid_); assert(view_info->status == WSREP_VIEW_NON_PRIMARY); // Draining monitors could hang when the state is corrupt as // there may be blocked appliers. if (not st_.corrupt()) { drain_monitors_for_local_conf_change(); } update_incoming_list(*view_info); try { submit_view_info(recv_ctx, view_info); free(view_info); } catch (gu::Exception& e) { free(view_info); log_fatal << e.what(); abort(); } { gu::Lock lock(closing_mutex_); if (state_() > S_CONNECTED) { state_.shift_to(S_CONNECTED); } } } static void validate_local_prim_view_info(const wsrep_view_info_t* view_info, const wsrep_uuid_t& my_uuid) { assert(view_info->status == WSREP_VIEW_PRIMARY); if (view_info->memb_num > 0 && (view_info->my_idx < 0 || view_info->my_idx >= view_info->memb_num)) // something went wrong, member must be present in own view { std::ostringstream msg; msg << "Node UUID " << my_uuid << " is absent from the view:\n"; for (int m(0); m < view_info->memb_num; ++m) { msg << '\t' << view_info->members[m].id << '\n'; } msg << "most likely due to unexpected node identity change. " "Aborting."; log_fatal << msg.str(); abort(); } } bool galera::ReplicatorSMM::skip_prim_conf_change( const wsrep_view_info_t& view_info, int const proto_ver) { wsrep_seqno_t cc_seqno(WSREP_SEQNO_UNDEFINED); bool keep(false); // keep in cache if (proto_ver >= PROTO_VER_ORDERED_CC) { cc_seqno = view_info.state_id.seqno; if (cc_seqno > cert_.position()) { // was not part of IST preload, adjust cert. index // see handle_trx_overlapping_ist() for analogous logic assert(cc_seqno == cert_.position() + 1); const int trx_ver (std::get<0>(get_trx_protocol_versions(proto_ver))); cert_.adjust_position(view_info, gu::GTID(view_info.state_id.uuid, cc_seqno), trx_ver); keep = true; } } log_info << "####### skipping local CC " << cc_seqno << ", keep in cache: " << (keep ? "true" : "false"); return keep; } void galera::ReplicatorSMM::process_first_view( const wsrep_view_info_t* view_info, const wsrep_uuid_t& new_uuid) { assert(uuid_ == WSREP_UUID_UNDEFINED && new_uuid != WSREP_UUID_UNDEFINED); assert(view_info->state_id.uuid != WSREP_UUID_UNDEFINED); uuid_ = new_uuid; log_info << "Process first view: " << view_info->state_id.uuid << " my uuid: " << new_uuid; if (connected_cb_) { wsrep_cb_status_t cret(connected_cb_(app_ctx_, view_info)); if (cret != WSREP_CB_SUCCESS) { log_fatal << "Application returned error " << cret << " from connect callback, aborting"; abort(); } } } void galera::ReplicatorSMM::process_group_change( const wsrep_view_info_t* view_info) { assert(state_uuid_ != view_info->state_id.uuid); log_info << "Process group change: " << state_uuid_ << " -> " << view_info->state_id.uuid; if (connected_cb_) { wsrep_cb_status_t cret(connected_cb_(app_ctx_, view_info)); if (cret != WSREP_CB_SUCCESS) { log_fatal << "Application returned error " << cret << " from connect callback, aborting"; abort(); } } } void galera::ReplicatorSMM::process_st_required( void* recv_ctx, int const group_proto_ver, const wsrep_view_info_t* view_info) { const wsrep_seqno_t group_seqno(view_info->state_id.seqno); const wsrep_uuid_t& group_uuid (view_info->state_id.uuid); void* app_req(0); size_t app_req_len(0); #ifndef NDEBUG bool app_waits_sst(false); #endif log_info << "State transfer required: " << "\n\tGroup state: " << group_uuid << ":" << group_seqno << "\n\tLocal state: " << state_uuid_<< ":" << last_committed(); if (S_CONNECTED != state_()) state_.shift_to(S_CONNECTED); wsrep_cb_status_t const rcode(sst_request_cb_(app_ctx_, &app_req, &app_req_len)); if (WSREP_CB_SUCCESS != rcode) { assert(app_req_len <= 0); log_fatal << "SST request callback failed. This is unrecoverable, " << "restart required."; abort(); } else if (0 == app_req_len && state_uuid_ != group_uuid) { log_fatal << "Local state UUID " << state_uuid_ << " is different from group state UUID " << group_uuid << ", and SST request is null: restart required."; abort(); } #ifndef NDEBUG app_waits_sst = (app_req_len > 0) && (app_req_len != (strlen(WSREP_STATE_TRANSFER_NONE) + 1) || memcmp(app_req, WSREP_STATE_TRANSFER_NONE, app_req_len)); log_info << "App waits SST: " << app_waits_sst; #endif // GCache::seqno_reset() happens here request_state_transfer (recv_ctx, group_proto_ver, group_uuid, group_seqno, app_req, app_req_len); free(app_req); finish_local_prim_conf_change(group_proto_ver, group_seqno, "sst"); // No need to submit view info. It is always contained either // in SST or applied in IST. } void galera::ReplicatorSMM::reset_index_if_needed( const wsrep_view_info_t* view_info, int const prev_protocol_version, int const next_protocol_version, bool const st_required) { const wsrep_seqno_t group_seqno(view_info->state_id.seqno); const wsrep_uuid_t& group_uuid (view_info->state_id.uuid); // // Starting from protocol_version_ 8 joiner's cert index is rebuilt // from IST. // // The reasons to reset cert index: // - Protocol version lower than PROTO_VER_ORDERED_CC (ALL) // - Protocol upgrade (ALL) // - State transfer will take place (JOINER) // bool index_reset(next_protocol_version < PROTO_VER_ORDERED_CC || prev_protocol_version != next_protocol_version || // this last condition is a bit too strict. In fact // checking for app_waits_sst would be enough, but in // that case we'd have to skip cert index rebuilding // when there is none. // This would complicate the logic with little to no // benefits... st_required); if (index_reset) { gu::GTID position; int trx_proto_ver; if (next_protocol_version < PROTO_VER_ORDERED_CC) { position.set(group_uuid, group_seqno); trx_proto_ver = std::get<0>(get_trx_protocol_versions( next_protocol_version)); } else { position = gu::GTID(); // With PROTO_VER_ORDERED_CC/index preload the cert protocol version // is adjusted during IST/cert index preload. // See process_ist_conf_change(). trx_proto_ver = -1; } // Index will be reset, so all write sets preceding this CC in // local order must be discarded. Therefore the pending cert queue // must also be cleared. pending_cert_queue_.clear(); /* 2 reasons for this here: * 1 - compatibility with protocols < PROTO_VER_ORDERED_CC * 2 - preparing cert index for preloading by setting seqno to 0 */ log_info << "Cert index reset to " << position << " (proto: " << next_protocol_version << "), state transfer needed: " << (st_required ? "yes" : "no"); /* flushes service thd, must be called before gcache_.seqno_reset()*/ cert_.assign_initial_position(position, trx_proto_ver); } else { log_info << "Skipping cert index reset"; } } void galera::ReplicatorSMM::shift_to_next_state(Replicator::State next_state) { if (state_() == S_CONNECTED || state_() == S_DONOR) { switch (next_state) { case S_JOINING: state_.shift_to(S_JOINING); break; case S_DONOR: if (state_() == S_CONNECTED) { state_.shift_to(S_DONOR); } break; case S_JOINED: state_.shift_to(S_JOINED); break; case S_SYNCED: state_.shift_to(S_SYNCED); if (synced_cb_(app_ctx_) != WSREP_CB_SUCCESS) { log_fatal << "Synced callback failed. This is " << "unrecoverable, restart required."; abort(); } break; default: log_debug << "next_state " << next_state; break; } } st_.set(state_uuid_, WSREP_SEQNO_UNDEFINED, safe_to_bootstrap_); } void galera::ReplicatorSMM::become_joined_if_needed() { if (state_() == S_JOINING && sst_state_ != SST_NONE) { /* There are two reasons we can be here: * 1) we just got state transfer in request_state_transfer(). * 2) we failed here previously (probably due to partition). */ try { gcs_.join(gu::GTID(state_uuid_, sst_seqno_), 0); sst_state_ = SST_JOIN_SENT; } catch (gu::Exception& e) { if (e.get_errno() == ENOTCONN) { log_info << "Failed to JOIN due to non-Prim"; } else { log_warn << "Failed to JOIN the cluster after SST " << e.what(); } } } } void galera::ReplicatorSMM::submit_ordered_view_info( void* recv_ctx, const wsrep_view_info_t* view_info) { try { submit_view_info(recv_ctx, view_info); } catch (gu::Exception& e) { log_fatal << e.what(); abort(); } } void galera::ReplicatorSMM::finish_local_prim_conf_change( int const group_proto_ver __attribute__((unused)), wsrep_seqno_t const seqno, const char* context) { become_joined_if_needed(); record_cc_seqnos(seqno, context); // GCache must contain some actions, at least this CC bool const ordered __attribute__((unused)) (group_proto_ver >= PROTO_VER_ORDERED_CC); assert(gcache_.seqno_min() > 0 || not ordered); } namespace { // Deleter for view_info. struct ViewInfoDeleter { void operator()(void* ptr) { ::free(ptr); } }; } void galera::ReplicatorSMM::process_prim_conf_change(void* recv_ctx, const gcs_act_cchange& conf, int const my_index, void* cc_buf) { assert(conf.seqno > 0); assert(my_index >= 0); GU_DBUG_SYNC_WAIT("process_primary_configuration"); // Helper to discard cc_buf automatically when it goes out of scope. // Method keep() should be called if the buffer should be kept in // gcache. class CcBufDiscard { public: CcBufDiscard(gcache::GCache& gcache, void* cc_buf) : gcache_(gcache) , cc_buf_(cc_buf) { } CcBufDiscard(const CcBufDiscard&) = delete; CcBufDiscard& operator=(const CcBufDiscard&) = delete; ~CcBufDiscard() { if (cc_buf_) gcache_.free(cc_buf_); } void keep(wsrep_seqno_t const cc_seqno) // keep cc_buf_ in gcache_ { gu_trace(gcache_.seqno_assign(cc_buf_, cc_seqno, GCS_ACT_CCHANGE, false)); cc_buf_ = 0; } private: gcache::GCache& gcache_; void* cc_buf_; } cc_buf_discard(gcache_, cc_buf); // Processing local primary conf change, so this node must always // be in conf change as indicated by my_index. assert(my_index >= 0); int const group_proto_version(conf.repl_proto_ver); wsrep_uuid_t new_uuid(uuid_); auto const view_info(std::unique_ptr( galera_view_info_create( conf, capabilities(group_proto_version), my_index, new_uuid))); assert(view_info->my_idx == my_index); // Will abort if validation fails validate_local_prim_view_info(view_info.get(), uuid_); bool const ordered(group_proto_version >= PROTO_VER_ORDERED_CC); const wsrep_uuid_t& group_uuid (view_info->state_id.uuid); wsrep_seqno_t const group_seqno(view_info->state_id.seqno); assert(group_seqno == conf.seqno); assert(not ordered || group_seqno > 0); /* Invalidate sst_seqno_ in case of group change. */ if (state_uuid_ != group_uuid) sst_seqno_ = WSREP_SEQNO_UNDEFINED; if (conf.seqno <= sst_seqno_) { // contained already in SST, skip any further processing if (skip_prim_conf_change(*view_info, group_proto_version)) { // was not part of IST, don't discard cc_buf_discard.keep(conf.seqno); } return; } log_info << "####### processing CC " << group_seqno << ", local" << (ordered ? ", ordered" : ", unordered"); drain_monitors_for_local_conf_change(); int const prev_protocol_version(protocol_version_); const bool first_view(uuid_ == WSREP_UUID_UNDEFINED); if (first_view) { process_first_view(view_info.get(), new_uuid); } else if (state_uuid_ != group_uuid) { process_group_change(view_info.get()); } log_info << "####### My UUID: " << uuid_; safe_to_bootstrap_ = (view_info->memb_num == 1); gcs_node_state_t const my_state(conf.memb[my_index].state_); assert(my_state > GCS_NODE_STATE_NON_PRIM); assert(my_state < GCS_NODE_STATE_MAX); update_incoming_list(*view_info); bool const st_required (state_transfer_required(*view_info, group_proto_version, my_state == GCS_NODE_STATE_PRIM)); Replicator::State const next_state(state2repl(my_state, my_index)); // if protocol version >= PROTO_VER_ORDERED_CC, first CC already // carries seqno 1, so it can't be less than 1. For older protocols // it can be 0. assert(group_seqno >= (group_proto_version >= PROTO_VER_ORDERED_CC)); reset_index_if_needed(view_info.get(), prev_protocol_version, group_proto_version, st_required); if (st_required) { process_st_required(recv_ctx, group_proto_version, view_info.get()); // Rolling upgrade from earlier version. Group protocol version // PROTO_VER_GALERA_3_MAX and below do not get CCs from the IST, // so protocol versions are not established at this point yet. // Do it now before continuing. if (group_proto_version <= PROTO_VER_GALERA_3_MAX) { establish_protocol_versions(group_proto_version); } return; } // From this point on the CC is known to be processed in order. assert(group_seqno > cert_.position()); // This CC is processed in order. Establish protocol versions, // it must be done before cert_.adjust_position(). establish_protocol_versions (group_proto_version); /* since CC does not pass certification, need to adjust cert * position explicitly (when processed in order) */ /* flushes service thd, must be called before gcache_.seqno_reset()*/ cert_.adjust_position(*view_info, gu::GTID(group_uuid, group_seqno), trx_params_.version_); if (first_view) { /* if CC is ordered need to use preceding seqno */ set_initial_position(group_uuid, group_seqno - ordered); gcache_.seqno_reset(gu::GTID(group_uuid, group_seqno - ordered)); } else { // Note: Monitor initial position setting is not needed as this CC // is processed in order. assert(state_uuid_ == group_uuid); update_state_uuid(group_uuid); } /* CCs from IST already have seqno assigned and cert. position * adjusted */ if (ordered) { cc_buf_discard.keep(group_seqno); } shift_to_next_state(next_state); submit_ordered_view_info(recv_ctx, view_info.get()); finish_local_prim_conf_change(group_proto_version, group_seqno, "group"); // Cancel monitors after view event has been processed by the // application. Otherwise last_committed_id() will return incorrect // value if called from view callback. if (ordered) cancel_seqno(group_seqno); } void galera::ReplicatorSMM::process_join(wsrep_seqno_t seqno_j, wsrep_seqno_t seqno_l) { LocalOrder lo(seqno_l); gu_trace(local_monitor_.enter(lo)); wsrep_seqno_t const upto(cert_.position()); drain_monitors(upto); if (seqno_j < 0 && S_JOINING == state_()) { // #595, @todo: find a way to re-request state transfer log_fatal << "Failed to receive state transfer: " << seqno_j << " (" << gcs_state_transfer_error_str(-seqno_j) << "), need to restart."; abort(); } else { state_.shift_to(S_JOINED); sst_state_ = SST_NONE; } local_monitor_.leave(lo); } void galera::ReplicatorSMM::process_sync(wsrep_seqno_t seqno_l) { LocalOrder lo(seqno_l); gu_trace(local_monitor_.enter(lo)); wsrep_seqno_t const upto(cert_.position()); drain_monitors(upto); state_.shift_to(S_SYNCED); if (synced_cb_(app_ctx_) != WSREP_CB_SUCCESS) { log_fatal << "Synced callback failed. This is unrecoverable, " << "restart required."; abort(); } local_monitor_.leave(lo); } wsrep_seqno_t galera::ReplicatorSMM::pause() { // Grab local seqno for local_monitor_ wsrep_seqno_t const local_seqno( static_cast(gcs_.local_sequence())); LocalOrder lo(local_seqno); local_monitor_.enter(lo); // Local monitor should take care that concurrent // pause requests are enqueued assert(pause_seqno_ == WSREP_SEQNO_UNDEFINED); pause_seqno_ = local_seqno; // Get drain seqno from cert index wsrep_seqno_t const upto(cert_.position()); drain_monitors(upto); assert (apply_monitor_.last_left() >= upto); if (co_mode_ != CommitOrder::BYPASS) { assert (commit_monitor_.last_left() >= upto); assert (commit_monitor_.last_left() == apply_monitor_.last_left()); } wsrep_seqno_t const ret(last_committed()); st_.set(state_uuid_, ret, safe_to_bootstrap_); log_info << "Provider paused at " << state_uuid_ << ':' << ret << " (" << pause_seqno_ << ")"; return ret; } void galera::ReplicatorSMM::resume() { if (pause_seqno_ == WSREP_SEQNO_UNDEFINED) { log_warn << "tried to resume unpaused provider"; return; } st_.set(state_uuid_, WSREP_SEQNO_UNDEFINED, safe_to_bootstrap_); log_info << "resuming provider at " << pause_seqno_; LocalOrder lo(pause_seqno_); pause_seqno_ = WSREP_SEQNO_UNDEFINED; local_monitor_.leave(lo); log_info << "Provider resumed."; } void galera::ReplicatorSMM::desync() { wsrep_seqno_t seqno_l; ssize_t const ret(gcs_.desync(seqno_l)); if (seqno_l > 0) { LocalOrder lo(seqno_l); // need to process it regardless of ret value if (ret == 0) { /* #706 - the check below must be state request-specific. We are not holding any locks here and must be able to wait like any other action. However practice may prove different, leaving it here as a reminder. if (local_monitor_.would_block(seqno_l)) { gu_throw_error (-EDEADLK) << "Ran out of resources waiting to " << "desync the node. " << "The node must be restarted."; } */ local_monitor_.enter(lo); if (state_() != S_DONOR) state_.shift_to(S_DONOR); local_monitor_.leave(lo); } else { local_monitor_.self_cancel(lo); } } if (ret) { gu_throw_error(-ret) << gcs_error_str(-ret); } } void galera::ReplicatorSMM::resync() { gcs_.join(gu::GTID(state_uuid_, commit_monitor_.last_left()), 0); } ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// //// Private ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// /* process pending queue events scheduled before seqno */ void galera::ReplicatorSMM::process_pending_queue(wsrep_seqno_t local_seqno) { // This method should be called only from code paths of local // processing, i.e. events from group. assert(local_seqno > 0); // pending_cert_queue_ contains all writesets that: // a) were BF aborted before being certified // b) are not going to be replayed because of not having // commit flag set // // Before certifying the current seqno, check if // pending_cert_queue contains any smaller seqno. // This avoids the certification index to diverge // across nodes. TrxHandleSlavePtr queued_ts; while ((queued_ts = pending_cert_queue_.must_cert_next(local_seqno)) != 0) { log_debug << "must cert next " << local_seqno << " aborted ts " << *queued_ts; Certification::TestResult const result(cert_.append_trx(queued_ts)); log_debug << "trx in pending cert queue certified, result: " << result; assert(!queued_ts->cert_bypass() || Certification::TestResult::TEST_OK == result); bool const skip(Certification::TestResult::TEST_FAILED == result && !(queued_ts->cert_bypass()/* expl. ROLLBACK */)); /* at this point we are still assigning seqno to buffer in order */ gcache_.seqno_assign(queued_ts->action().first, queued_ts->global_seqno(), GCS_ACT_WRITESET, skip); cert_.set_trx_committed(*queued_ts); } } bool galera::ReplicatorSMM::enter_local_monitor_for_cert( TrxHandleMaster* trx, const TrxHandleSlavePtr& ts) { bool in_replay(trx != 0 && trx->state() == TrxHandle::S_MUST_REPLAY); bool interrupted(false); try { if (trx != 0) { if (in_replay == false) TX_SET_STATE(*trx, TrxHandle::S_CERTIFYING); trx->unlock(); } LocalOrder lo(*ts); gu_trace(local_monitor_.enter(lo)); if (trx != 0) trx->lock(); assert(trx == 0 || (trx->state() == TrxHandle::S_CERTIFYING || trx->state() == TrxHandle::S_MUST_ABORT || trx->state() == TrxHandle::S_MUST_REPLAY)); TX_SET_STATE(*ts, TrxHandle::S_CERTIFYING); } catch (gu::Exception& e) { if (trx != 0) trx->lock(); if (e.get_errno() == EINTR) { interrupted = true; } else throw; } return (not interrupted); } wsrep_status_t galera::ReplicatorSMM::handle_local_monitor_interrupted( TrxHandleMaster* trx, const TrxHandleSlavePtr& ts) { assert(trx != 0); // Did not enter local monitor. assert(ts->state() == TrxHandle::S_REPLICATING); wsrep_status_t retval = WSREP_BF_ABORT; assert(ts->state() == TrxHandle::S_REPLICATING || ts->state() == TrxHandle::S_CERTIFYING); assert(trx != 0); // If the transaction was committing, it must replay. if (ts->flags() & TrxHandle::F_COMMIT) { // Return immediately without canceling local monitor, // it needs to be grabbed again in replay stage. TX_SET_STATE(*trx, TrxHandle::S_MUST_REPLAY); return retval; } // if not - we need to rollback, so pretend that certification // failed, but still update cert index to match slaves else { pending_cert_queue_.push(ts); retval = WSREP_TRX_FAIL; } assert(WSREP_TRX_FAIL == retval); TX_SET_STATE(*trx, TrxHandle::S_ABORTING); LocalOrder lo(*ts); local_monitor_.self_cancel(lo); // Cert for aborted returned certification failure, so this // trx will roll back. Mark it as certified to denote that // local monitor must not be accessed again. TX_SET_STATE(*ts, TrxHandle::S_CERTIFYING); assert((retval == WSREP_TRX_FAIL && ts->is_dummy()) || retval == WSREP_BF_ABORT || ts->queued()); return retval; } wsrep_status_t galera::ReplicatorSMM::finish_cert( TrxHandleMaster* trx, const TrxHandleSlavePtr& ts) { assert(ts->state() == TrxHandle::S_CERTIFYING); gu_trace(process_pending_queue(ts->local_seqno())); // Write sets which would overlap with IST must have already been // filtered out before getting here. assert(ts->global_seqno() == cert_.position() + 1); wsrep_status_t retval; switch (cert_.append_trx(ts)) { case Certification::TEST_OK: // NBO_END should certify positively only if it ends NBO assert(ts->ends_nbo() > 0 || !ts->nbo_end()); if (trx != 0 && trx->state() == TrxHandle::S_MUST_ABORT) { if (ts->flags() & TrxHandle::F_COMMIT) { TX_SET_STATE(*trx, TrxHandle::S_MUST_REPLAY); // apply monitor will be entered during replay } else { // Abort the transaction if non-committing // fragment was BF aborted during certification. TX_SET_STATE(*trx, TrxHandle::S_ABORTING); } retval = WSREP_BF_ABORT; } else { retval = WSREP_OK; } assert(!ts->is_dummy()); break; case Certification::TEST_FAILED: assert(ts->is_dummy()); if (ts->nbo_end()) assert(ts->ends_nbo() == WSREP_SEQNO_UNDEFINED); local_cert_failures_ += ts->local(); if (trx != 0) TX_SET_STATE(*trx, TrxHandle::S_ABORTING); retval = WSREP_TRX_FAIL; break; default: retval = WSREP_TRX_FAIL; assert(0); break; } // we must do seqno assignment 'in order' for std::map reasons, // so keeping it inside the monitor. NBO end should never be skipped. bool const skip(ts->is_dummy() && !ts->nbo_end()); gcache_.seqno_assign (ts->action().first, ts->global_seqno(), GCS_ACT_WRITESET, skip); LocalOrder lo(*ts); local_monitor_.leave(lo); return retval; } /* don't use this directly, use cert_and_catch() instead */ inline wsrep_status_t galera::ReplicatorSMM::cert(TrxHandleMaster* trx, const TrxHandleSlavePtr& ts) { assert(trx == 0 || (trx->state() == TrxHandle::S_REPLICATING || trx->state() == TrxHandle::S_MUST_REPLAY)); assert(ts->state() == TrxHandle::S_REPLICATING); assert(ts->local_seqno() != WSREP_SEQNO_UNDEFINED); assert(ts->global_seqno() != WSREP_SEQNO_UNDEFINED); assert(ts->last_seen_seqno() >= 0); assert(ts->last_seen_seqno() < ts->global_seqno()); // Verify checksum before certification to avoid corrupting index. ts->verify_checksum(); LocalOrder lo(*ts); // Local monitor is either released or canceled in // handle_local_monitor_interrupted(), finish_cert(). bool interrupted(not enter_local_monitor_for_cert(trx, ts)); if (gu_unlikely (interrupted)) { return handle_local_monitor_interrupted(trx, ts); } else { return finish_cert(trx, ts); } } /* pretty much any exception in cert() is fatal as it blocks local_monitor_ */ wsrep_status_t galera::ReplicatorSMM::cert_and_catch( TrxHandleMaster* trx, const TrxHandleSlavePtr& ts) { try { return cert(trx, ts); } catch (std::exception& e) { log_fatal << "Certification exception: " << e.what(); } catch (...) { log_fatal << "Unknown certification exception"; } assert(0); abort(); } bool galera::ReplicatorSMM::enter_apply_monitor_for_local( TrxHandleMaster& trx, const TrxHandleSlavePtr& ts) { assert(ts->global_seqno() > last_committed()); assert(ts->depends_seqno() >= 0); TX_SET_STATE(trx, TrxHandle::S_APPLYING); ApplyOrder ao(*ts); bool interrupted(false); try { trx.unlock(); GU_DBUG_SYNC_WAIT("before_certify_apply_monitor_enter"); gu_trace(apply_monitor_.enter(ao)); GU_DBUG_SYNC_WAIT("after_certify_apply_monitor_enter"); trx.lock(); assert(trx.state() == TrxHandle::S_APPLYING || trx.state() == TrxHandle::S_MUST_ABORT); } catch (gu::Exception& e) { trx.lock(); if (e.get_errno() == EINTR) { interrupted = true; } else throw; } return (not interrupted); } wsrep_status_t galera::ReplicatorSMM::handle_apply_monitor_interrupted( TrxHandleMaster& trx, const TrxHandleSlavePtr& ts) { assert(trx.state() == TrxHandle::S_MUST_ABORT); assert(ts->state() == TrxHandle::S_CERTIFYING); wsrep_status_t retval; if (ts->flags() & TrxHandle::F_COMMIT) { TX_SET_STATE(trx, TrxHandle::S_MUST_REPLAY); retval = WSREP_BF_ABORT; } else { TX_SET_STATE(trx, TrxHandle::S_ABORTING); retval = WSREP_TRX_FAIL; } return retval; } void galera::ReplicatorSMM::enter_apply_monitor_for_local_not_committing( const TrxHandleMaster& trx, TrxHandleSlave& ts) { assert(trx.state() == TrxHandle::S_ABORTING || trx.state() == TrxHandle::S_REPLAYING); assert(ts.state() < TrxHandle::S_COMMITTING); switch (ts.state()) { case TrxHandle::S_REPLICATING: TX_SET_STATE(ts, TrxHandle::S_CERTIFYING); // fall through case TrxHandle::S_CERTIFYING: { ApplyOrder ao(ts); apply_monitor_.enter(ao); TX_SET_STATE(ts, TrxHandle::S_APPLYING); break; } case TrxHandle::S_APPLYING: break; default: assert(0); // Should never happen } } void galera::ReplicatorSMM::update_state_uuid (const wsrep_uuid_t& uuid) { if (state_uuid_ != uuid) { *(const_cast(&state_uuid_)) = uuid; std::ostringstream os; os << state_uuid_; // Copy only non-nil terminated part of the source string // and terminate the string explicitly to silence a warning // generated by Wstringop-truncation char* str(const_cast(state_uuid_str_)); strncpy(str, os.str().c_str(), sizeof(state_uuid_str_) - 1); str[sizeof(state_uuid_str_) - 1] = '\0'; } st_.set(uuid, WSREP_SEQNO_UNDEFINED, safe_to_bootstrap_); } void galera::ReplicatorSMM::abort() { log_info << "ReplicatorSMM::abort()"; gcs_.close(); gu_abort(); } galera-4-26.4.22/galera/src/key_entry_ng.hpp000644 000162 177776 00000007273 14755062442 021753 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2013-2018 Codership Oy // #ifndef GALERA_KEY_ENTRY_NG_HPP #define GALERA_KEY_ENTRY_NG_HPP #include "trx_handle.hpp" #include namespace galera { class KeyEntryNG { public: KeyEntryNG(const KeySet::KeyPart& key) : refs_{nullptr, nullptr, nullptr, nullptr}, #ifndef NDEBUG seqnos_{0, 0, 0, 0}, #endif // NDEBUG key_(key) { } KeyEntryNG(const KeyEntryNG& other) : refs_(other.refs_) , #ifndef NDEBUG seqnos_(other.seqnos_) , #endif /* NDEBUG */ key_(other.key_) { } const KeySet::KeyPart& key() const { return key_; } void ref(wsrep_key_type_t p, const KeySet::KeyPart& k, TrxHandleSlave* trx) { assert(p <= KeySet::Key::TYPE_MAX); assert(0 == refs_[p] || refs_[p]->global_seqno() <= trx->global_seqno()); refs_[p] = trx; #ifndef NDEBUG seqnos_[p] = trx->global_seqno(); #endif // NDEBUG key_ = k; } void unref(wsrep_key_type_t p, const TrxHandleSlave* trx) { assert(p <= KeySet::Key::TYPE_MAX); assert(refs_[p] != NULL); if (refs_[p] == trx) { refs_[p] = NULL; } else { assert(refs_[p]->global_seqno() > trx->global_seqno()); assert(0); } } bool referenced() const { for (auto i : refs_) { if (i != nullptr) return true; } return false; } void for_each_ref(const std::function& fn) const { for (auto i : refs_) { fn(i); } } const TrxHandleSlave* ref_trx(wsrep_key_type_t const p) const { assert(p <= KeySet::Key::TYPE_MAX); return refs_[p]; } size_t size() const { return sizeof(*this); } void swap(KeyEntryNG& other) throw() { std::swap(refs_, other.refs_); #ifndef NDEBUG std::swap(seqnos_, other.seqnos_); #endif /* NDEBUG */ std::swap(key_, other.key_); } KeyEntryNG& operator=(KeyEntryNG ke) { swap(ke); return *this; } ~KeyEntryNG() { assert(!referenced()); } private: std::array refs_; #ifndef NDEBUG std::array seqnos_; #endif // NDEBUG KeySet::KeyPart key_; }; inline void swap(KeyEntryNG& a, KeyEntryNG& b) { a.swap(b); } class KeyEntryHashNG { public: size_t operator()(const KeyEntryNG& ke) const { return ke.key().hash(); } }; class KeyEntryPtrHashNG { public: size_t operator()(const KeyEntryNG* const ke) const { return ke->key().hash(); } }; class KeyEntryEqualNG { public: bool operator()(const KeyEntryNG& left, const KeyEntryNG& right) const { return left.key().matches(right.key()); } }; class KeyEntryPtrEqualNG { public: bool operator()(const KeyEntryNG* const left, const KeyEntryNG* const right) const { return left->key().matches(right->key()); } }; } #endif // GALERA_KEY_ENTRY_HPP galera-4-26.4.22/galera/src/replicator_smm_stats.cpp000644 000162 177776 00000033706 14755062442 023507 0ustar00jenkinsnogroup000000 000000 /* Copyright (C) 2010-2017 Codership Oy */ #include "replicator_smm.hpp" #include #include // @todo: should be protected static member of the parent class static wsrep_member_status_t state2stats(galera::ReplicatorSMM::State state) { switch (state) { case galera::ReplicatorSMM::S_DESTROYED : case galera::ReplicatorSMM::S_CLOSED : case galera::ReplicatorSMM::S_CONNECTED : return WSREP_MEMBER_UNDEFINED; case galera::ReplicatorSMM::S_JOINING : return WSREP_MEMBER_JOINER; case galera::ReplicatorSMM::S_JOINED : return WSREP_MEMBER_JOINED; case galera::ReplicatorSMM::S_SYNCED : return WSREP_MEMBER_SYNCED; case galera::ReplicatorSMM::S_DONOR : return WSREP_MEMBER_DONOR; } log_fatal << "Unknown state code: " << state; assert(0); return WSREP_MEMBER_ERROR; } // @todo: should be protected static member of the parent class static const char* state2stats_str(galera::ReplicatorSMM::State state, galera::ReplicatorSMM::SstState sst_state) { using galera::ReplicatorSMM; switch (state) { case galera::ReplicatorSMM::S_DESTROYED: return "Destroyed"; case galera::ReplicatorSMM::S_CLOSED: case galera::ReplicatorSMM::S_CONNECTED: { if (sst_state == ReplicatorSMM::SST_REQ_FAILED) return "Joining: State Transfer request failed"; else if (sst_state == ReplicatorSMM::SST_FAILED) return "Joining: State Transfer failed"; else return "Initialized"; } case galera::ReplicatorSMM::S_JOINING: { if (sst_state == ReplicatorSMM::SST_WAIT) return "Joining: receiving State Transfer"; else return "Joining"; } case galera::ReplicatorSMM::S_JOINED: return "Joined"; case galera::ReplicatorSMM::S_SYNCED: return "Synced"; case galera::ReplicatorSMM::S_DONOR: return "Donor/Desynced"; } log_fatal << "Unknown state: " << state; assert(0); return "Unknown state code: "; } typedef enum status_vars { STATS_STATE_UUID = 0, STATS_PROTOCOL_VERSION, STATS_PROTO_APPL, STATS_PROTO_REPL, STATS_PROTO_GCS, STATS_LAST_COMMITTED, STATS_REPLICATED, STATS_REPLICATED_BYTES, STATS_KEYS_COUNT, STATS_KEYS_BYTES, STATS_DATA_BYTES, STATS_UNRD_BYTES, STATS_RECEIVED, STATS_RECEIVED_BYTES, STATS_LOCAL_COMMITS, STATS_LOCAL_CERT_FAILURES, STATS_LOCAL_REPLAYS, STATS_LOCAL_SEND_QUEUE, STATS_LOCAL_SEND_QUEUE_MAX, STATS_LOCAL_SEND_QUEUE_MIN, STATS_LOCAL_SEND_QUEUE_AVG, STATS_LOCAL_RECV_QUEUE, STATS_LOCAL_RECV_QUEUE_MAX, STATS_LOCAL_RECV_QUEUE_MIN, STATS_LOCAL_RECV_QUEUE_AVG, STATS_LOCAL_CACHED_DOWNTO, STATS_FC_PAUSED_NS, STATS_FC_PAUSED_AVG, STATS_FC_SSENT, // STATS_FC_CSENT, STATS_FC_RECEIVED, STATS_FC_ACTIVE, STATS_FC_REQUESTED, STATS_CERT_DEPS_DISTANCE, STATS_APPLY_OOOE, STATS_APPLY_OOOL, STATS_APPLY_WINDOW, STATS_APPLY_WAITS, STATS_COMMIT_OOOE, STATS_COMMIT_OOOL, STATS_COMMIT_WINDOW, STATS_LOCAL_STATE, STATS_LOCAL_STATE_COMMENT, STATS_CERT_INDEX_SIZE, STATS_CAUSAL_READS, STATS_CERT_INTERVAL, STATS_OPEN_TRX, STATS_OPEN_CONN, STATS_INCOMING_LIST, STATS_MAX } StatusVars; static const struct wsrep_stats_var wsrep_stats[STATS_MAX + 1] = { { "local_state_uuid", WSREP_VAR_STRING, { 0 } }, { "protocol_version", WSREP_VAR_INT64, { 0 } }, { "protocol_application", WSREP_VAR_INT64, { 0 } }, { "protocol_replicator", WSREP_VAR_INT64, { 0 } }, { "protocol_GCS", WSREP_VAR_INT64, { 0 } }, { "last_committed", WSREP_VAR_INT64, { -1 } }, { "replicated", WSREP_VAR_INT64, { 0 } }, { "replicated_bytes", WSREP_VAR_INT64, { 0 } }, { "repl_keys", WSREP_VAR_INT64, { 0 } }, { "repl_keys_bytes", WSREP_VAR_INT64, { 0 } }, { "repl_data_bytes", WSREP_VAR_INT64, { 0 } }, { "repl_other_bytes", WSREP_VAR_INT64, { 0 } }, { "received", WSREP_VAR_INT64, { 0 } }, { "received_bytes", WSREP_VAR_INT64, { 0 } }, { "local_commits", WSREP_VAR_INT64, { 0 } }, { "local_cert_failures", WSREP_VAR_INT64, { 0 } }, { "local_replays", WSREP_VAR_INT64, { 0 } }, { "local_send_queue", WSREP_VAR_INT64, { 0 } }, { "local_send_queue_max", WSREP_VAR_INT64, { 0 } }, { "local_send_queue_min", WSREP_VAR_INT64, { 0 } }, { "local_send_queue_avg", WSREP_VAR_DOUBLE, { 0 } }, { "local_recv_queue", WSREP_VAR_INT64, { 0 } }, { "local_recv_queue_max", WSREP_VAR_INT64, { 0 } }, { "local_recv_queue_min", WSREP_VAR_INT64, { 0 } }, { "local_recv_queue_avg", WSREP_VAR_DOUBLE, { 0 } }, { "local_cached_downto", WSREP_VAR_INT64, { 0 } }, { "flow_control_paused_ns", WSREP_VAR_INT64, { 0 } }, { "flow_control_paused", WSREP_VAR_DOUBLE, { 0 } }, { "flow_control_sent", WSREP_VAR_INT64, { 0 } }, // { "flow_control_conts_sent", WSREP_VAR_INT64, { 0 } }, { "flow_control_recv", WSREP_VAR_INT64, { 0 } }, { "flow_control_active", WSREP_VAR_STRING, { 0 } }, { "flow_control_requested", WSREP_VAR_STRING, { 0 } }, { "cert_deps_distance", WSREP_VAR_DOUBLE, { 0 } }, { "apply_oooe", WSREP_VAR_DOUBLE, { 0 } }, { "apply_oool", WSREP_VAR_DOUBLE, { 0 } }, { "apply_window", WSREP_VAR_DOUBLE, { 0 } }, { "apply_waits", WSREP_VAR_INT64, { 0 } }, { "commit_oooe", WSREP_VAR_DOUBLE, { 0 } }, { "commit_oool", WSREP_VAR_DOUBLE, { 0 } }, { "commit_window", WSREP_VAR_DOUBLE, { 0 } }, { "local_state", WSREP_VAR_INT64, { 0 } }, { "local_state_comment", WSREP_VAR_STRING, { 0 } }, { "cert_index_size", WSREP_VAR_INT64, { 0 } }, { "causal_reads", WSREP_VAR_INT64, { 0 } }, { "cert_interval", WSREP_VAR_DOUBLE, { 0 } }, { "open_transactions", WSREP_VAR_INT64, { 0 } }, { "open_connections", WSREP_VAR_INT64, { 0 } }, { "incoming_addresses", WSREP_VAR_STRING, { 0 } }, { 0, WSREP_VAR_STRING, { 0 } } }; void galera::ReplicatorSMM::build_stats_vars ( std::vector& stats) { const struct wsrep_stats_var* ptr(wsrep_stats); do { stats.push_back(*ptr); } while (ptr++->name != 0); stats[STATS_STATE_UUID].value._string = state_uuid_str_; } const struct wsrep_stats_var* galera::ReplicatorSMM::stats_get() const { if (S_DESTROYED == state_()) return 0; std::vector sv(wsrep_stats_); sv[STATS_PROTOCOL_VERSION ].value._int64 = protocol_version_; wsrep_gtid last_committed; (void)last_committed_id(&last_committed); sv[STATS_LAST_COMMITTED ].value._int64 = last_committed.seqno; sv[STATS_REPLICATED ].value._int64 = replicated_(); sv[STATS_REPLICATED_BYTES ].value._int64 = replicated_bytes_(); sv[STATS_KEYS_COUNT ].value._int64 = keys_count_(); sv[STATS_KEYS_BYTES ].value._int64 = keys_bytes_(); sv[STATS_DATA_BYTES ].value._int64 = data_bytes_(); sv[STATS_UNRD_BYTES ].value._int64 = unrd_bytes_(); sv[STATS_RECEIVED ].value._int64 = as_->received(); sv[STATS_RECEIVED_BYTES ].value._int64 = as_->received_bytes(); sv[STATS_LOCAL_COMMITS ].value._int64 = local_commits_(); sv[STATS_LOCAL_CERT_FAILURES].value._int64 = local_cert_failures_(); sv[STATS_LOCAL_REPLAYS ].value._int64 = local_replays_(); struct gcs_stats stats; gcs_.get_stats (&stats); sv[STATS_PROTO_APPL ].value._int64 = stats.proto_appl; sv[STATS_PROTO_REPL ].value._int64 = stats.proto_repl; sv[STATS_PROTO_GCS ].value._int64 = stats.proto_gcs; sv[STATS_LOCAL_SEND_QUEUE ].value._int64 = stats.send_q_len; sv[STATS_LOCAL_SEND_QUEUE_MAX].value._int64 = stats.send_q_len_max; sv[STATS_LOCAL_SEND_QUEUE_MIN].value._int64 = stats.send_q_len_min; sv[STATS_LOCAL_SEND_QUEUE_AVG].value._double = stats.send_q_len_avg; sv[STATS_LOCAL_RECV_QUEUE ].value._int64 = stats.recv_q_len; sv[STATS_LOCAL_RECV_QUEUE_MAX].value._int64 = stats.recv_q_len_max; sv[STATS_LOCAL_RECV_QUEUE_MIN].value._int64 = stats.recv_q_len_min; sv[STATS_LOCAL_RECV_QUEUE_AVG].value._double = stats.recv_q_len_avg; sv[STATS_LOCAL_CACHED_DOWNTO ].value._int64 = gcache_.seqno_min(); sv[STATS_FC_PAUSED_NS ].value._int64 = stats.fc_paused_ns; sv[STATS_FC_PAUSED_AVG ].value._double = stats.fc_paused_avg; sv[STATS_FC_SSENT ].value._int64 = stats.fc_ssent; // sv[STATS_FC_CSENT ].value._int64 = stats.fc_csent; sv[STATS_FC_RECEIVED ].value._int64 = stats.fc_received; sv[STATS_FC_ACTIVE ].value._string = stats.fc_active ? "true" : "false"; sv[STATS_FC_REQUESTED ].value._string = stats.fc_requested ? "true" : "false"; double avg_cert_interval(0); double avg_deps_dist(0); size_t index_size(0); cert_.stats_get(avg_cert_interval, avg_deps_dist, index_size); sv[STATS_CERT_DEPS_DISTANCE ].value._double = avg_deps_dist; sv[STATS_CERT_INTERVAL ].value._double = avg_cert_interval; sv[STATS_CERT_INDEX_SIZE ].value._int64 = index_size; double oooe; double oool; double win; long long waits; apply_monitor_.get_stats(&oooe, &oool, &win, &waits); sv[STATS_APPLY_OOOE ].value._double = oooe; sv[STATS_APPLY_OOOL ].value._double = oool; sv[STATS_APPLY_WINDOW ].value._double = win; sv[STATS_APPLY_WAITS ].value._int64 = waits; commit_monitor_.get_stats(&oooe, &oool, &win, &waits); sv[STATS_COMMIT_OOOE ].value._double = oooe; sv[STATS_COMMIT_OOOL ].value._double = oool; sv[STATS_COMMIT_WINDOW ].value._double = win; if (st_.corrupt()) { sv[STATS_LOCAL_STATE ].value._int64 = WSREP_MEMBER_ERROR; sv[STATS_LOCAL_STATE_COMMENT].value._string = "Inconsistent"; } else { sv[STATS_LOCAL_STATE ].value._int64 =state2stats(state_()); sv[STATS_LOCAL_STATE_COMMENT].value._string =state2stats_str(state_(), sst_state_); } sv[STATS_CAUSAL_READS ].value._int64 = causal_reads_(); Wsdb::stats wsdb_stats(wsdb_.get_stats()); sv[STATS_OPEN_TRX].value._int64 = wsdb_stats.n_trx_; sv[STATS_OPEN_CONN].value._int64 = wsdb_stats.n_conn_; // Get gcs backend status gu::Status status; gcs_.get_status(status); #ifdef GU_DBUG_ON status.insert("debug_sync_waiters", gu_debug_sync_waiters()); #endif // GU_DBUG_ON // Dynamical strings are copied into buffer allocated after stats var array. // Compute space needed. size_t tail_size(0); for (gu::Status::const_iterator i(status.begin()); i != status.end(); ++i) { tail_size += i->first.size() + 1 + i->second.size() + 1; } gu::Lock lock_inc(incoming_mutex_); tail_size += incoming_list_.size() + 1; /* Create a buffer to be passed to the caller. */ // The buffer size needed: // * Space for wsrep_stats_ array // * Space for additional elements from status map // * Trailing space for string store size_t const vec_size( (sv.size() + status.size())*sizeof(struct wsrep_stats_var)); struct wsrep_stats_var* const buf(static_cast( gu_malloc(vec_size + tail_size))); if (buf) { // Resize sv to have enough space for variables from status sv.resize(sv.size() + status.size()); // Initial tail_buf position char* tail_buf(reinterpret_cast(buf + sv.size())); // Assign incoming list strncpy(tail_buf, incoming_list_.c_str(), incoming_list_.size() + 1); sv[STATS_INCOMING_LIST].value._string = tail_buf; tail_buf += incoming_list_.size() + 1; // Iterate over dynamical status variables and assign strings size_t sv_pos(STATS_INCOMING_LIST + 1); for (gu::Status::const_iterator i(status.begin()); i != status.end(); ++i, ++sv_pos) { // Name strncpy(tail_buf, i->first.c_str(), i->first.size() + 1); sv[sv_pos].name = tail_buf; tail_buf += i->first.size() + 1; // Type sv[sv_pos].type = WSREP_VAR_STRING; // Value strncpy(tail_buf, i->second.c_str(), i->second.size() + 1); sv[sv_pos].value._string = tail_buf; tail_buf += i->second.size() + 1; } assert(sv_pos == sv.size() - 1); // NULL terminate sv[sv_pos].name = 0; sv[sv_pos].type = WSREP_VAR_STRING; sv[sv_pos].value._string = 0; assert(static_cast (tail_buf - reinterpret_cast(buf)) == vec_size + tail_size); assert(reinterpret_cast(buf)[vec_size + tail_size - 1] == '\0'); // Finally copy sv vector to buf memcpy(buf, &sv[0], vec_size); } else { log_warn << "Failed to allocate stats vars buffer to " << (vec_size + tail_size) << " bytes. System is running out of memory."; } return buf; } void galera::ReplicatorSMM::stats_reset() { if (S_DESTROYED == state_()) return; gcs_.flush_stats (); apply_monitor_.flush_stats(); commit_monitor_.flush_stats(); cert_.stats_reset(); } void galera::ReplicatorSMM::stats_free(struct wsrep_stats_var* arg) { gu_free(arg); } galera-4-26.4.22/galera/src/key_data.hpp000644 000162 177776 00000003264 14755062442 021033 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2013-2024 Codership Oy // #ifndef GALERA_KEY_DATA_HPP #define GALERA_KEY_DATA_HPP #include "wsrep_api.h" #include namespace galera { struct KeyData { const wsrep_buf_t* const parts; int const parts_num; int const proto_ver; wsrep_key_type_t const type; bool const copy; KeyData (int const pv, const wsrep_buf_t* const k, int const kn, wsrep_key_type_t const tp, bool const cp) : parts (k), parts_num (kn), proto_ver (pv), type (tp), copy (cp) {} static wsrep_key_type_t const BRANCH_KEY_TYPE = WSREP_KEY_REFERENCE; /* Zero-level key constructor */ explicit KeyData (int const pv, wsrep_key_type_t const tp = BRANCH_KEY_TYPE) : parts (&zero_part), parts_num (1), proto_ver(pv), type (tp), copy(true) {} KeyData (const KeyData& kd) : parts (kd.parts), parts_num(kd.parts_num), proto_ver(kd.proto_ver), type (kd.type), copy (kd.copy) {} bool shared() const { return (type == WSREP_KEY_SHARED); } bool shared_or_ref() const { return (type <= WSREP_KEY_REFERENCE); } void print(std::ostream& os) const; private: KeyData& operator = (const KeyData&); /* an arbitrary constant for "zero"-level, server-wide key */ static wsrep_buf_t const zero_part; }; /* struct KeyData */ inline std::ostream& operator << (std::ostream& os, const KeyData& kd) { kd.print(os); return os; } } /* namespace galera */ #endif /* GALERA_KEY_DATA_HPP */ galera-4-26.4.22/galera/src/galera_info.hpp000644 000162 177776 00000001335 14755062442 021515 0ustar00jenkinsnogroup000000 000000 // Copyright (C) 2009-2018 Codership Oy #ifndef __GALERA_INFO_H__ #define __GALERA_INFO_H__ #include "gcs.hpp" #include "wsrep_api.h" /* create view info out of configuration message * if my_uuid is defined - use it to determine wsrep_view_info_t::my_idx, * otherwise set my_uuid according to my_idx */ extern wsrep_view_info_t* galera_view_info_create (const gcs_act_cchange& conf, wsrep_cap_t capabilities, int my_idx, wsrep_uuid_t& my_uuid); /* make a copy of view info object */ extern wsrep_view_info_t* galera_view_info_copy (const wsrep_view_info_t* vi); #endif // __GALERA_INFO_H__ galera-4-26.4.22/galera/src/action_source.hpp000644 000162 177776 00000000734 14755062442 022106 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2013 Codership Oy // #ifndef GALERA_ACTION_SOURCE_HPP #define GALERA_ACTION_SOURCE_HPP namespace galera { class ActionSource { public: ActionSource() { } virtual ~ActionSource() { } virtual ssize_t process(void* ctx, bool& exit_loop) = 0; virtual long long received() const = 0; virtual long long received_bytes() const = 0; }; } #endif // GALERA_ACTION_SOURCE_HPP galera-4-26.4.22/galera/src/galera_info.cpp000644 000162 177776 00000005042 14755062442 021507 0ustar00jenkinsnogroup000000 000000 // Copyright (C) 2009-2018 Codership Oy #include "galera_info.hpp" #include #include #include #include static size_t view_info_size (int members) { return (sizeof(wsrep_view_info_t) + members * sizeof(wsrep_member_info_t)); } /* create view info out of configuration message */ wsrep_view_info_t* galera_view_info_create (const gcs_act_cchange& conf, wsrep_cap_t const capabilities, int const my_idx, wsrep_uuid_t& my_uuid) { wsrep_view_info_t* ret = static_cast( ::malloc(view_info_size(conf.memb.size()))); if (ret) { wsrep_seqno_t const seqno (conf.seqno != GCS_SEQNO_ILL ? conf.seqno : WSREP_SEQNO_UNDEFINED); wsrep_gtid_t const gtid = { conf.uuid, seqno }; ret->state_id = gtid; ret->view = conf.conf_id; ret->status = conf.conf_id != -1 ? WSREP_VIEW_PRIMARY : WSREP_VIEW_NON_PRIMARY; ret->capabilities = capabilities; ret->my_idx = -1; ret->memb_num = conf.memb.size(); ret->proto_ver = conf.appl_proto_ver; for (int m = 0; m < ret->memb_num; ++m) { const gcs_act_cchange::member& cm(conf.memb[m]); // from wsrep_member_info_t& wm(ret->members[m]); // to wm.id = cm.uuid_; if (wm.id == my_uuid) { ret->my_idx = m; } strncpy(wm.name, cm.name_.c_str(), sizeof(wm.name) - 1); wm.name[sizeof(wm.name) - 1] = '\0'; strncpy(wm.incoming, cm.incoming_.c_str(), sizeof(wm.incoming) - 1); wm.incoming[sizeof(wm.incoming) - 1] = '\0'; } if (WSREP_UUID_UNDEFINED == my_uuid && my_idx >= 0) { assert(-1 == ret->my_idx); ret->my_idx = my_idx; assert(ret->my_idx < ret->memb_num); my_uuid = ret->members[ret->my_idx].id; } } else { gu_throw_error(ENOMEM) << "Failed to allocate galera view info"; } return ret; } /* make a copy of view info object */ wsrep_view_info_t* galera_view_info_copy (const wsrep_view_info_t* vi) { size_t ret_size = view_info_size (vi->memb_num); wsrep_view_info_t* ret = static_cast(malloc (ret_size)); if (ret) { memcpy (ret, vi, ret_size); } return ret; } galera-4-26.4.22/galera/src/CMakeLists.txt000644 000162 177776 00000010571 14755062442 021300 0ustar00jenkinsnogroup000000 000000 # # Copyright (C) 2020-2021 Codership Oy # add_library(galera STATIC mapped_buffer.cpp write_set.cpp data_set.cpp key_set.cpp write_set_ng.cpp trx_handle.cpp key_entry_os.cpp key_data.cpp wsdb.cpp certification.cpp galera_service_thd.cpp wsrep_params.cpp replicator_smm_params.cpp gcs_action_source.cpp galera_info.cpp galera_view.cpp replicator.cpp ist.cpp ist_proto.cpp gcs_dummy.cpp saved_state.cpp replicator_smm.cpp replicator_str.cpp replicator_smm_stats.cpp ) target_include_directories(galera PRIVATE ${PROJECT_SOURCE_DIR}/wsrep/src ) # TODO: Fix. target_compile_options(galera PRIVATE -Wno-conversion -Wno-unused-parameter ) if (GALERA_STATIC) target_link_libraries(galera gcs -static-libgcc) else() target_link_libraries(galera gcs) endif() add_library(galera_smm_static STATIC wsrep_provider.cpp key_data.cpp ) target_compile_definitions(galera_smm_static PRIVATE -DGALERA_VER="${GALERA_VERSION}" -DGALERA_REV="${GALERA_GIT_REVISION}" -DGALERA_MULTIMASTER ) target_include_directories(galera_smm_static PRIVATE ${PROJECT_SOURCE_DIR}/wsrep/src ) # TODO: Fix. target_compile_options(galera_smm_static PRIVATE -Wno-conversion -Wno-unused-parameter ) target_link_libraries(galera_smm_static galera) add_library(galera_smm MODULE wsrep_provider.cpp ) set_target_properties(galera_smm PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR} ) target_include_directories(galera_smm PRIVATE ${PROJECT_SOURCE_DIR}/wsrep/src ) target_compile_definitions(galera_smm PRIVATE -DGALERA_VER="${GALERA_VERSION}" -DGALERA_REV="${GALERA_GIT_REVISION}" -DGALERA_MULTIMASTER ) # TODO: Fix. target_compile_options(galera_smm PRIVATE -Wno-conversion -Wno-unused-parameter ) if (GALERA_VERSION_SCRIPT) # Limit symbols visible from Galera DSO. # Doing this allows to: # - make the ABI more clean and concise # - hide symbols from commonly used libraries (boost, asio, etc.), which # binds calls inside the DSO to its own versions of these libraries # See: https://akkadia.org/drepper/dsohowto.pdf (section 2.2.5) set(GALERA_LINK_OPTIONS -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/galera-sym.map) endif() target_link_libraries(galera_smm galera ${GALERA_LINK_OPTIONS}) install(TARGETS galera_smm DESTINATION lib) # The following checks are guaranteed to work only # Linux platform, we skip them on others. if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") return() endif() # # Post build checks: # - Check that symbol visibility matches GALERA_VERSION_SCRIPT. # - Check that SSL linkage matches GALERA_HAVE_SSL and GALERA_STATIC. # if (GALERA_VERSION_SCRIPT) add_custom_command(TARGET galera_smm POST_BUILD COMMAND sh -c "! ${CMAKE_OBJDUMP} -T libgalera_smm.so | grep asio" WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" COMMENT "Checking library symbol visibility (hidden)" VERBATIM) else() set(GALERA_LINK_OPTIONS "") add_custom_command(TARGET galera_smm POST_BUILD COMMAND sh -c "${CMAKE_OBJDUMP} -T libgalera_smm.so | grep asio" WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" COMMENT "Checking library symbol visibility (not hidden)" VERBATIM) endif() if (NOT GALERA_WITH_SSL) message(STATUS "Building Galera without SSL") add_custom_command(TARGET galera_smm POST_BUILD COMMAND sh -c "! (${CMAKE_OBJDUMP} -x libgalera_smm.so | grep NEEDED.*crypto) && ! (${CMAKE_OBJDUMP} -x libgalera_smm.so | grep NEEDED.*ssl)" WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" COMMENT "Verifying that library is not linked with SSL" VERBATIM) else() if (GALERA_STATIC) message(STATUS "Building Galera with static SSL") add_custom_command(TARGET galera_smm POST_BUILD COMMAND sh -c "(${CMAKE_OBJDUMP} -t libgalera_smm.so | grep OPENSSL) && (${CMAKE_OBJDUMP} -t libgalera_smm.so | grep CRYPTO)" WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" COMMENT "Verifying that library has OpenSSL linked statically" VERBATIM) else() message(STATUS "Building Galera with SSL") add_custom_command(TARGET galera_smm POST_BUILD COMMAND sh -c "(${CMAKE_OBJDUMP} -x libgalera_smm.so | grep NEEDED.*crypto) && (${CMAKE_OBJDUMP} -x libgalera_smm.so | grep NEEDED.*ssl)" COMMENT "Verifying that library is linked with SSL" WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" VERBATIM) endif() endif() galera-4-26.4.22/galera/src/ist.hpp000644 000162 177776 00000013435 14755062442 020052 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2011-2021 Codership Oy // #ifndef GALERA_IST_HPP #define GALERA_IST_HPP #include "wsrep_api.h" #include "galera_gcs.hpp" #include "trx_handle.hpp" #include "gu_config.hpp" #include "gu_lock.hpp" #include "gu_monitor.hpp" #include "gu_asio.hpp" #include #include namespace gcache { class GCache; } namespace galera { namespace ist { void register_params(gu::Config& conf); struct Result { int error; std::string error_str; Result(int error_arg, const std::string& error_str_arg) : error{error_arg} , error_str{error_str_arg} { } }; // IST event handler interface class EventHandler { public: // Process transaction from IST virtual void ist_trx(const TrxHandleSlavePtr&, bool must_apply, bool preload) = 0; // Process conf change from IST virtual void ist_cc(const gcs_action&, bool must_apply, bool preload) = 0; // Report IST end virtual void ist_end(const Result&) = 0; protected: virtual ~EventHandler() {} }; class Receiver { public: static std::string const RECV_ADDR; static std::string const RECV_BIND; Receiver(gu::Config& conf, gcache::GCache&, TrxHandleSlave::Pool& slave_pool, EventHandler&, const char* addr, gu::Progress::Callback* callback); ~Receiver(); std::string prepare(wsrep_seqno_t first_seqno, wsrep_seqno_t last_seqno, int protocol_version, const wsrep_uuid_t& source_id); // this must be called AFTER SST is processed and we know // the starting point. void ready(wsrep_seqno_t first); wsrep_seqno_t finished(); void run(); wsrep_seqno_t first_seqno() const { return first_seqno_; } private: void interrupt(); std::string recv_addr_; std::string recv_bind_; gu::AsioIoService io_service_; std::shared_ptr acceptor_; gu::Mutex mutex_; gu::Cond cond_; gu::Progress::Callback* progress_cb_; wsrep_seqno_t first_seqno_; wsrep_seqno_t last_seqno_; wsrep_seqno_t current_seqno_; gu::Config& conf_; gcache::GCache& gcache_; TrxHandleSlave::Pool& slave_pool_; wsrep_uuid_t source_id_; EventHandler& handler_; gu_thread_t thread_; int error_code_; int version_; bool use_ssl_; bool running_; bool ready_; // GCC 4.8.5 on FreeBSD wants this Receiver(const Receiver&); Receiver& operator=(const Receiver&); }; class Sender { public: Sender(const gu::Config& conf, gcache::GCache& gcache, const std::string& peer, int version); virtual ~Sender(); // first - first trx seqno // last - last trx seqno // preload_start - the seqno from which sent transactions // are accompanied with index preload flag void send(wsrep_seqno_t first, wsrep_seqno_t last, wsrep_seqno_t preload_start); void cancel() { socket_->close(); } private: gu::AsioIoService io_service_; std::shared_ptr socket_; const gu::Config& conf_; gcache::GCache& gcache_; int version_; bool use_ssl_; Sender(const Sender&); void operator=(const Sender&); }; class AsyncSender; class AsyncSenderMap { public: AsyncSenderMap(gcache::GCache& gcache) : senders_(), monitor_(), gcache_(gcache) { } void run(const gu::Config& conf, const std::string& peer, wsrep_seqno_t first, wsrep_seqno_t last, wsrep_seqno_t preload_start, int version); void remove(AsyncSender*, wsrep_seqno_t); void cancel(); gcache::GCache& gcache() { return gcache_; } private: std::set senders_; // use monitor instead of mutex, it provides cancellation point gu::Monitor monitor_; gcache::GCache& gcache_; }; } // namespace ist // Helpers to determine receive addr and receive bind. Public for // testing. std::string IST_determine_recv_addr(gu::Config& conf); std::string IST_determine_recv_bind(gu::Config& conf); } // namespace galera #endif // GALERA_IST_HPP galera-4-26.4.22/galera/src/key_data.cpp000644 000162 177776 00000000763 14755062442 021027 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2023 Codership Oy // #include "key_data.hpp" #include void galera::KeyData::print(std::ostream& os) const { os << "proto: " << proto_ver << ", type: " << type << ", copy: " << (copy ? "yes" : "no") << ", parts(" << parts_num << "):"; for (int i = 0; i < parts_num; ++i) { os << "\n\t" << gu::Hexdump(parts[i].ptr, parts[i].len, true); } } const wsrep_buf_t galera::KeyData::zero_part = { "", 1 }; galera-4-26.4.22/galera/src/key_os.hpp000644 000162 177776 00000022052 14755062442 020537 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2011-2013 Codership Oy // #ifndef GALERA_KEY_HPP #define GALERA_KEY_HPP #include "wsrep_api.h" #include "gu_hash.h" #include "gu_serialize.hpp" #include "gu_unordered.hpp" #include "gu_throw.hpp" #include "gu_logger.hpp" #include "gu_vlq.hpp" #include #include #include #include #include namespace galera { // helper to cast from any kind of pointer to void template static inline void* void_cast(const C* c) { return const_cast(reinterpret_cast(c)); } class KeyPartOS { public: KeyPartOS(const gu::byte_t* buf, size_t buf_size) : buf_(buf), buf_size_(buf_size) { } const gu::byte_t* buf() const { return buf_; } size_t size() const { return buf_size_; } size_t key_len() const { #ifndef GALERA_KEY_VLQ return buf_[0]; #else size_t ret; (void)gu::uleb128_decode(buf_, buf_size_, 0, ret); return ret; #endif } #ifndef GALERA_KEY_VLQ const gu::byte_t* key() const { return buf_ + 1; } #else const gu::byte_t* key() const { size_t not_used; return buf_ + gu::uleb128_decode(buf_, buf_size_, 0, not_used); } #endif bool operator==(const KeyPartOS& other) const { return (other.buf_size_ == buf_size_ && memcmp(other.buf_, buf_, buf_size_) == 0); } private: const gu::byte_t* buf_; size_t buf_size_; }; inline std::ostream& operator<<(std::ostream& os, const KeyPartOS& kp) { const std::ostream::fmtflags prev_flags(os.flags(std::ostream::hex)); const char prev_fill(os.fill('0')); for (const gu::byte_t* i(kp.key()); i != kp.key() + kp.key_len(); ++i) { os << std::setw(2) << static_cast(*i); } os.flags(prev_flags); os.fill(prev_fill); return os; } class KeyOS { public: enum { F_SHARED = 0x1 }; KeyOS(int version) : version_(version), flags_(), keys_() { } KeyOS(int version, const wsrep_buf_t* keys, size_t keys_len, uint8_t flags) : version_(version), flags_ (flags), keys_ () { if (keys_len > 255) { gu_throw_error(EINVAL) << "maximum number of key parts exceeded: " << keys_len; } switch (version) { case 1: case 2: for (size_t i(0); i < keys_len; ++i) { size_t const offset(keys_.size()); size_t key_len(keys[i].len); const gu::byte_t* base(reinterpret_cast( keys[i].ptr)); #ifndef GALERA_KEY_VLQ if (gu_unlikely(key_len > 0xff)) key_len = 0xff; keys_.reserve(offset + 1 + key_len); keys_.insert(keys_.end(), key_len); keys_.insert(keys_.end(), base, base + key_len); #else size_t len_size(gu::uleb128_size(key_len)); keys_.resize(offset + len_size); (void)gu::uleb128_encode( key_len, &keys_[0], keys_.size(), offset); keys_.insert(keys_.end(), base, base + keys[i].key_len); #endif } break; default: gu_throw_fatal << "unsupported key version: " << version_; } } template KeyOS(int version, Ci begin, Ci end, uint8_t flags) : version_(version), flags_(flags), keys_() { for (Ci i(begin); i != end; ++i) { keys_.insert( keys_.end(), i->buf(), i->buf() + i->size()); } } int version() const { return version_; } template C key_parts() const { C ret; size_t i(0); size_t const keys_size(keys_.size()); while (i < keys_size) { #ifndef GALERA_KEY_VLQ size_t key_len(keys_[i] + 1); #else size_t key_len; size_t offset( gu::uleb128_decode(&keys_[0], keys_size, i, key_len)); key_len += offset - i; #endif if (gu_unlikely((i + key_len) > keys_size)) { gu_throw_fatal << "Keys buffer overflow by " << i + key_len - keys_size << " bytes: " << i + key_len << '/' << keys_size; } KeyPartOS kp(&keys_[i], key_len); ret.push_back(kp); i += key_len; } assert(i == keys_size); return ret; } uint8_t flags() const { return flags_; } bool operator==(const KeyOS& other) const { return (keys_ == other.keys_); } bool equal_all(const KeyOS& other) const { return (version_ == other.version_ && flags_ == other.flags_ && keys_ == other.keys_); } size_t size() const { return keys_.size() + sizeof(*this); } size_t hash() const { return gu_table_hash(keys_.data(), keys_.size()); } size_t hash_with_flags() const { return hash() ^ gu_table_hash(&flags_, sizeof(flags_)); } size_t serialize(gu::byte_t*, size_t, size_t) const; size_t unserialize(const gu::byte_t*, size_t, size_t); size_t serial_size() const; private: friend std::ostream& operator<<(std::ostream& os, const KeyOS& key); int version_; uint8_t flags_; gu::Buffer keys_; }; inline std::ostream& operator<<(std::ostream& os, const KeyOS& key) { std::ostream::fmtflags flags(os.flags()); switch (key.version_) { case 2: os << std::hex << static_cast(key.flags()) << " "; // Fall through case 1: { std::deque dq(key.key_parts >()); std::copy(dq.begin(), dq.end(), std::ostream_iterator(os, " ")); break; } default: gu_throw_fatal << "unsupported key version: " << key.version_; } os.flags(flags); return os; } inline size_t KeyOS::serialize(gu::byte_t* buf, size_t buflen, size_t offset) const { switch (version_) { #ifndef GALERA_KEY_VLQ case 1: return gu::serialize2(keys_, buf, buflen, offset); case 2: offset = gu::serialize1(flags_, buf, buflen, offset); return gu::serialize2(keys_, buf, buflen, offset); #else case 1: { size_t keys_size(keys_.size()); offset = gu::uleb128_encode(keys_size, buf, buflen, offset); assert (offset + key_size <= buflen); std::copy(&keys_[0], &keys_[0] + keys_size, buf + offset); return (offset + keys_size); } #endif default: log_fatal << "Internal error: unsupported key version: " << version_; abort(); return 0; } } inline size_t KeyOS::unserialize(const gu::byte_t* buf, size_t buflen, size_t offset) { switch (version_) { #ifndef GALERA_KEY_VLQ case 1: return gu::unserialize2(buf, buflen, offset, keys_); case 2: offset = gu::unserialize1(buf, buflen, offset, flags_); return gu::unserialize2(buf, buflen, offset, keys_); #else case 1: { size_t len; offset = gu::uleb128_decode(buf, buflen, offset, len); keys_.resize(len); std::copy(buf + offset, buf + offset + len, keys_.begin()); return (offset + len); } #endif default: gu_throw_error(EPROTONOSUPPORT) << "unsupported key version: " << version_; } } inline size_t KeyOS::serial_size() const { switch (version_) { #ifndef GALERA_KEY_VLQ case 1: return gu::serial_size2(keys_); case 2: return (gu::serial_size(flags_) + gu::serial_size2(keys_)); #else case 1: { size_t size(gu::uleb128_size(keys_.size())); return (size + keys_.size()); } #endif default: log_fatal << "Internal error: unsupported key version: " << version_; abort(); return 0; } } } #endif // GALERA_KEY_HPP galera-4-26.4.22/galera/src/fsm.hpp000644 000162 177776 00000004656 14755062442 020045 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2021 Codership Oy // #ifndef GALERA_FSM_HPP #define GALERA_FSM_HPP #include "gu_unordered.hpp" #include "gu_throw.hpp" #include #include namespace galera { template class FSM { public: typedef gu::UnorderedSet TransMap; typedef std::pair StateEntry; FSM(State const initial_state) : delete_(true), trans_map_(new TransMap), state_(initial_state, 0), state_hist_() { } FSM(TransMap* const trans_map, State const initial_state) : delete_(false), trans_map_(trans_map), state_(initial_state, 0), state_hist_() { } ~FSM() { if (delete_ == true) delete trans_map_; } void shift_to(State const state, int const line = -1) { auto i = trans_map_->find(Transition(state_.first, state)); if (i == trans_map_->end()) { log_fatal << "FSM: no such a transition " << state_.first << " -> " << state; abort(); // we want to catch it in the stack } StateEntry const se(state, line); state_hist_.push_back(state_); state_ = se; } void force(State const state) { state_ = StateEntry(state, 0); } void reset_history() { state_hist_.clear(); } const State& operator()() const { return state_.first; } const StateEntry& get_state_entry() const { return state_; } void add_transition(Transition const& trans) { if (trans_map_->insert(trans).second == false) { gu_throw_fatal << "transition " << trans.from() << " -> " << trans.to() << " already exists"; } } const std::vector& history() const { return state_hist_; } private: FSM(const FSM&); void operator=(const FSM&); bool delete_; TransMap* const trans_map_; StateEntry state_; std::vector state_hist_; }; } #endif // GALERA_FSM_HPP galera-4-26.4.22/galera/src/galera_service_thd.cpp000644 000162 177776 00000007603 14755062442 023060 0ustar00jenkinsnogroup000000 000000 /* * Copyright (C) 2010-2013 Codership Oy * * Using broadcasts instead of signals below to wake flush callers due to * theoretical possibility of more than 2 threads involved. */ #include "galera_service_thd.hpp" const uint32_t galera::ServiceThd::A_NONE = 0; static const uint32_t A_LAST_COMMITTED = 1U << 0; static const uint32_t A_RELEASE_SEQNO = 1U << 1; static const uint32_t A_FLUSH = 1U << 30; static const uint32_t A_EXIT = 1U << 31; void* galera::ServiceThd::thd_func (void* arg) { galera::ServiceThd* st = reinterpret_cast(arg); bool exit = false; while (!exit) { galera::ServiceThd::Data data; { gu::Lock lock(st->mtx_); if (A_NONE == st->data_.act_) lock.wait(st->cond_); data = st->data_; st->data_.act_ = A_NONE; // clear pending actions if (data.act_ & A_FLUSH) { if (A_FLUSH == data.act_) { // no other actions scheduled (all previous are "flushed") log_info << "Service thread queue flushed."; st->flush_.broadcast(); } else { // restore flush flag for the next iteration st->data_.act_ |= A_FLUSH; } } } exit = ((data.act_ & A_EXIT)); if (!exit) { if (data.act_ & A_LAST_COMMITTED) { ssize_t const ret (st->gcs_.set_last_applied(data.last_committed_)); if (gu_unlikely(ret < 0)) { // @todo: figure out what to do in this case } else { log_debug << "Reported last committed: " << data.last_committed_; } } if (data.act_ & A_RELEASE_SEQNO) { try { st->gcache_.seqno_release(data.release_seqno_); } catch (std::exception& e) { log_warn << "Exception releasing seqno " << data.release_seqno_ << ": " << e.what(); } } } } return 0; } galera::ServiceThd::ServiceThd (GcsI& gcs, gcache::GCache& gcache) : gcache_ (gcache), gcs_ (gcs), thd_ (), mtx_ (), cond_ (), flush_ (), data_ () { gu_thread_create (&thd_, NULL, thd_func, this); } galera::ServiceThd::~ServiceThd () { { gu::Lock lock(mtx_); data_.act_ = A_EXIT; cond_.signal(); flush_.broadcast(); } gu_thread_join(thd_, NULL); } void galera::ServiceThd::flush(const gu::UUID& uuid) { gu::Lock lock(mtx_); if (!(data_.act_ & A_EXIT)) { if (data_.act_ == A_NONE) cond_.signal(); data_.act_ |= A_FLUSH; do { lock.wait(flush_); } while (data_.act_ & A_FLUSH); } data_.last_committed_.set(uuid); } void galera::ServiceThd::reset() { gu::Lock lock(mtx_); data_.act_ = A_NONE; data_.last_committed_ = gu::GTID(); } void galera::ServiceThd::report_last_committed(gcs_seqno_t const seqno, bool const report) { gu::Lock lock(mtx_); if (gu_likely(data_.last_committed_.seqno() < seqno)) { data_.last_committed_.set(seqno); if (gu_likely(report)) { if (data_.act_ == A_NONE) cond_.signal(); data_.act_ |= A_LAST_COMMITTED; } } } void galera::ServiceThd::release_seqno(gcs_seqno_t seqno) { gu::Lock lock(mtx_); if (data_.release_seqno_ < seqno) { data_.release_seqno_ = seqno; if (data_.act_ == A_NONE) cond_.signal(); data_.act_ |= A_RELEASE_SEQNO; } } galera-4-26.4.22/galera/src/monitor.hpp000644 000162 177776 00000041237 14755062442 020743 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2013 Codership Oy // #ifndef GALERA_MONITOR_HPP #define GALERA_MONITOR_HPP #include "trx_handle.hpp" #include // for gu::Mutex and gu::Cond #include #include namespace galera { template class Monitor { private: struct Process { Process() : obj_(0), cond_(), wait_cond_(), state_(S_IDLE) #ifndef NDEBUG ,dobj_() #endif /* NDEBUG */ { } const C* obj_; gu::Cond cond_; gu::Cond wait_cond_; enum State { S_IDLE, // Slot is free S_WAITING, // Waiting to enter applying critical section S_CANCELED, S_APPLYING, // Applying S_FINISHED // Finished } state_; #ifndef NDEBUG C dobj_; #endif /* NDEBUG */ private: // non-copyable Process(const Process& other); void operator=(const Process&); }; static const ssize_t process_size_ = (1ULL << 16); static const size_t process_mask_ = process_size_ - 1; public: Monitor() : mutex_(), cond_(), uuid_(WSREP_UUID_UNDEFINED), last_entered_(-1), last_left_(-1), drain_seqno_(GU_LLONG_MAX), process_(new Process[process_size_]), entered_(0), oooe_(0), oool_(0), win_size_(0), waits_(0) { } ~Monitor() { delete[] process_; if (entered_ > 0) { log_info << "mon: entered " << entered_ << " oooe fraction " << double(oooe_)/entered_ << " oool fraction " << double(oool_)/entered_; } else { log_info << "apply mon: entered 0"; } } /* * For ordered CC events this had to be changed: * - it either resets position to -1 or * - merely advances it to seqno if current position is behind. * Assumes that monitor has been drained. */ void set_initial_position(const wsrep_uuid_t& uuid, wsrep_seqno_t const seqno) { gu::Lock lock(mutex_); state_debug_print("set_initial_position", seqno); uuid_ = uuid; // When the monitor position is reset, either all the // waiters must have been drained or the thread which is // resetting the position must hold the monitor (CC from IST). // Exception is -1 which means that the monitor is being // forcifully reset. assert(seqno == -1 || last_entered_ == last_left_ || last_entered_ == seqno); if (last_entered_ == -1 || seqno == -1) { // first call or reset last_entered_ = last_left_ = seqno; } else #if 1 // now { if (last_left_ < seqno) last_left_ = seqno; if (last_entered_ < last_left_) last_entered_ = last_left_; } // some drainers may wait for us here cond_.broadcast(); #else // before { // drain monitor up to seqno but don't reset last_entered_ // or last_left_ drain_common(seqno, lock); drain_seqno_ = GU_LLONG_MAX; } #endif if (seqno != -1) { const size_t idx(indexof(seqno)); process_[idx].wait_cond_.broadcast(); } } void enter(C& obj) { const wsrep_seqno_t obj_seqno(obj.seqno()); const size_t idx(indexof(obj_seqno)); gu::Lock lock(mutex_); state_debug_print("enter", obj); assert(obj_seqno > last_left_); pre_enter(obj, lock); if (gu_likely(process_[idx].state_ != Process::S_CANCELED)) { assert(process_[idx].state_ == Process::S_IDLE); process_[idx].state_ = Process::S_WAITING; process_[idx].obj_ = &obj; #ifndef NDEBUG process_[idx].dobj_.~C(); new (&process_[idx].dobj_) C(obj); #endif /* NDEBUG */ #ifdef GU_DBUG_ON obj.debug_sync(mutex_); #endif // GU_DBUG_ON while (may_enter(obj) == false && process_[idx].state_ == Process::S_WAITING) { ++waits_; lock.wait(process_[idx].cond_); } if (process_[idx].state_ != Process::S_CANCELED) { assert(process_[idx].state_ == Process::S_WAITING || process_[idx].state_ == Process::S_APPLYING); process_[idx].state_ = Process::S_APPLYING; ++entered_; oooe_ += ((last_left_ + 1) < obj_seqno); win_size_ += (last_entered_ - last_left_); return; } } assert(process_[idx].state_ == Process::S_CANCELED); process_[idx].state_ = Process::S_IDLE; state_debug_print("enter canceled", obj); gu_throw_error(EINTR); } bool entered(const C& obj) const { return state(obj) == Process::S_APPLYING; } bool finished(const C& obj) const { return state(obj) == Process::S_FINISHED; } bool canceled(const C& obj) const { return state(obj) == Process::S_CANCELED; } void leave(const C& obj) { #ifndef NDEBUG size_t idx(indexof(obj.seqno())); #endif /* NDEBUG */ gu::Lock lock(mutex_); state_debug_print("leave", obj); assert(process_[idx].state_ == Process::S_APPLYING || process_[idx].state_ == Process::S_CANCELED); assert(process_[indexof(last_left_)].state_ == Process::S_IDLE); post_leave(obj.seqno(), lock); } void self_cancel(C& obj) { wsrep_seqno_t const obj_seqno(obj.seqno()); size_t idx(indexof(obj_seqno)); gu::Lock lock(mutex_); state_debug_print("self_cancel", obj); assert(obj_seqno > last_left_); while (obj_seqno - last_left_ >= process_size_) // TODO: exit on error { log_warn << "Trying to self-cancel seqno out of process " << "space: obj_seqno - last_left_ = " << obj_seqno << " - " << last_left_ << " = " << (obj_seqno - last_left_) << ", process_size_: " << process_size_ << ". Deadlock is very likely."; lock.wait(cond_); } assert(process_[idx].state_ == Process::S_IDLE || process_[idx].state_ == Process::S_CANCELED); #ifndef NDEBUG process_[idx].dobj_.~C(); new (&process_[idx].dobj_) C(obj); #endif /* NDEBUG */ if (obj_seqno > last_entered_) last_entered_ = obj_seqno; if (obj_seqno <= drain_seqno_) { post_leave(obj.seqno(), lock); } else { process_[idx].state_ = Process::S_FINISHED; } } bool interrupt(const C& obj) { size_t idx (indexof(obj.seqno())); gu::Lock lock(mutex_); while (obj.seqno() - last_left_ >= process_size_) // TODO: exit on error { lock.wait(cond_); } state_debug_print("interrupt", obj); if ((process_[idx].state_ == Process::S_IDLE && obj.seqno() > last_left_ ) || process_[idx].state_ == Process::S_WAITING ) { process_[idx].state_ = Process::S_CANCELED; process_[idx].cond_.signal(); // since last_left + 1 cannot be <= S_WAITING we're not // modifying a window here. No broadcasting. return true; } else { log_debug << "interrupting " << obj.seqno() << " state " << process_[idx].state_ << " le " << last_entered_ << " ll " << last_left_; } return false; } wsrep_seqno_t last_left() const { gu::Lock lock(mutex_); return last_left_; } wsrep_seqno_t last_entered() const { gu::Lock lock(mutex_); return last_entered_; } void last_left_gtid(wsrep_gtid_t& gtid) const { gu::Lock lock(mutex_); gtid.uuid = uuid_; gtid.seqno = last_left_; } ssize_t size() const { return process_size_; } bool would_block (wsrep_seqno_t seqno) const { return (seqno - last_left_ >= process_size_ || seqno > drain_seqno_); } void drain(wsrep_seqno_t seqno) { gu::Lock lock(mutex_); state_debug_print("drain", seqno); while (drain_seqno_ != GU_LLONG_MAX) { lock.wait(cond_); } drain_common(seqno, lock); // there can be some stale canceled entries update_last_left(); drain_seqno_ = GU_LLONG_MAX; cond_.broadcast(); } void wait(wsrep_seqno_t seqno) { gu::Lock lock(mutex_); while (last_left_ < seqno) { size_t idx(indexof(seqno)); lock.wait(process_[idx].wait_cond_); } } void wait(gu::GTID& gtid, const gu::datetime::Date& wait_until) { gu::Lock lock(mutex_); if (gtid.uuid() != uuid_) { throw gu::NotFound(); } while (last_left_ < gtid.seqno()) { size_t idx(indexof(gtid.seqno())); lock.wait(process_[idx].wait_cond_, wait_until); } } void get_stats(double* oooe, double* oool, double* win_size, long long* waits) const { gu::Lock lock(mutex_); if (entered_ > 0) { *oooe = (oooe_ > 0 ? double(oooe_)/entered_ : .0); *oool = (oool_ > 0 ? double(oool_)/entered_ : .0); *win_size = (win_size_ > 0 ? double(win_size_)/entered_ : .0); } else { *oooe = .0; *oool = .0; *win_size = .0; } *waits = waits_; } void flush_stats() { gu::Lock lock(mutex_); oooe_ = 0; oool_ = 0; win_size_ = 0; entered_ = 0; waits_ = 0; } private: template void state_debug_print(const std::string& method, const T& x) { // #define GALERA_MONITOR_DEBUG_PRINT #ifdef GALERA_MONITOR_DEBUG_PRINT log_info << typeid(C).name() << ": " << method << "(" << x << "): le " << last_entered_ << ", ll " << last_left_; #endif /* GALERA_MONITOR_DEBUG_PRINT */ } size_t indexof(wsrep_seqno_t seqno) const { return (seqno & process_mask_); } bool may_enter(const C& obj) const { return obj.condition(last_entered_, last_left_); } // wait until it is possible to grab slot in monitor, // update last entered void pre_enter(C& obj, gu::Lock& lock) { assert(last_left_ <= last_entered_); const wsrep_seqno_t obj_seqno(obj.seqno()); while (would_block (obj_seqno)) // TODO: exit on error { lock.wait(cond_); } if (last_entered_ < obj_seqno) last_entered_ = obj_seqno; } void update_last_left() { for (wsrep_seqno_t i = last_left_ + 1; i <= last_entered_; ++i) { Process& a(process_[indexof(i)]); if (Process::S_FINISHED == a.state_) { a.state_ = Process::S_IDLE; last_left_ = i; a.wait_cond_.broadcast(); } else { break; } } assert(last_left_ <= last_entered_); } void wake_up_next() { for (wsrep_seqno_t i = last_left_ + 1; i <= last_entered_; ++i) { Process& a(process_[indexof(i)]); if (a.state_ == Process::S_WAITING && may_enter(*a.obj_) == true) { // We need to set state to APPLYING here because if // it is the last_left_ + 1 and it gets canceled in // the race that follows exit from this function, // there will be nobody to clean up and advance // last_left_. a.state_ = Process::S_APPLYING; a.cond_.signal(); } } } void post_leave(wsrep_seqno_t const obj_seqno, gu::Lock& lock) { const size_t idx(indexof(obj_seqno)); if (last_left_ + 1 == obj_seqno) // we're shrinking window { process_[idx].state_ = Process::S_IDLE; last_left_ = obj_seqno; process_[idx].wait_cond_.broadcast(); update_last_left(); oool_ += (last_left_ > obj_seqno); // wake up waiters that may remain above us (last_left_ // now is max) wake_up_next(); } else { process_[idx].state_ = Process::S_FINISHED; } process_[idx].obj_ = 0; assert((last_left_ >= obj_seqno && process_[idx].state_ == Process::S_IDLE) || process_[idx].state_ == Process::S_FINISHED); assert(last_left_ != last_entered_ || process_[indexof(last_left_)].state_ == Process::S_IDLE); if ((last_left_ >= obj_seqno) || // - occupied window shrinked (last_left_ >= drain_seqno_)) // - this is to notify drain that // we reached drain_seqno_ { cond_.broadcast(); } } void drain_common(wsrep_seqno_t seqno, gu::Lock& lock) { log_debug << "draining up to " << seqno; drain_seqno_ = seqno; if (last_left_ > drain_seqno_) { log_warn << "last left " << last_left_ << " greater than drain seqno " << drain_seqno_; #ifndef NDEBUG for (wsrep_seqno_t i = drain_seqno_; i <= last_left_; ++i) { const Process& a(process_[indexof(i)]); log_info << "applier " << i << " in state " << a.state_; } #endif } while (last_left_ < drain_seqno_) lock.wait(cond_); } typename Process::State state(const C& obj) const { const wsrep_seqno_t obj_seqno(obj.seqno()); const size_t idx(indexof(obj_seqno)); gu::Lock lock(mutex_); while (would_block (obj_seqno)) { lock.wait(cond_); } return process_[idx].state_; } Monitor(const Monitor&); void operator=(const Monitor&); mutable gu::Mutex mutex_; gu::Cond cond_; wsrep_uuid_t uuid_; wsrep_seqno_t last_entered_; wsrep_seqno_t last_left_; wsrep_seqno_t drain_seqno_; Process* process_; long entered_; // entered long oooe_; // out of order entered long oool_; // out of order left long win_size_; // window between last_left_ and last_entered_ // Total number of waits in the monitor. Incremented before // entering into waiting state. long long waits_; }; } #endif // GALERA_APPLY_MONITOR_HPP galera-4-26.4.22/galera/src/key_set.cpp000644 000162 177776 00000031224 14755062442 020705 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2013-2024 Codership Oy // #include "key_set.hpp" #include "gu_logger.hpp" #include "gu_hexdump.hpp" #include #include // std::transform namespace galera { void KeySet::throw_version(int ver) { gu_throw_error(EINVAL) << "Unsupported KeySet version: " << ver; } static const char* ver_str[KeySet::MAX_VERSION + 1] = { "EMPTY", "FLAT8", "FLAT8A", "FLAT16", "FLAT16A" }; KeySet::Version KeySet::version (const std::string& ver) { std::string tmp(ver); std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::toupper); for (int i(EMPTY); i <= MAX_VERSION; ++i) { if (tmp == ver_str[i]) return version(i); } gu_throw_error(EINVAL) << "Unsupported KeySet version: " << ver; throw; } static const char* type_str[4] = { "SH", "RE", "UP", "EX" }; const char* KeySet::type(wsrep_key_type_t t) { assert(size_t(t) < sizeof(type_str) / sizeof(type_str[0])); return type_str[t]; } size_t KeySet::KeyPart::store_annotation (const wsrep_buf_t* const parts, int const part_num, gu::byte_t* buf, int const size, int const alignment) { assert(size >= 0); /* max len representable in one byte */ static size_t const max_part_len(std::numeric_limits::max()); /* max multiple of alignment_ len representable in ann_size_t */ ann_size_t const max_ann_len(std::numeric_limits::max() / alignment * alignment); ann_size_t ann_size; int tmp_size(sizeof(ann_size)); for (int i(0); i <= part_num; ++i) { tmp_size += 1 + std::min(parts[i].len, max_part_len); } assert(tmp_size > 0); /* Make sure that final annotation size is * 1) is a multiple of alignment * 2) is representable with ann_size_t * 3) doesn't exceed dst buffer size */ ann_size = std::min(GU_ALIGN(tmp_size, alignment), max_ann_len); ann_size = std::min(ann_size, size / alignment * alignment); assert (ann_size <= size); assert ((ann_size % alignment) == 0); ann_size_t const pad_size(tmp_size < ann_size ? ann_size - tmp_size : 0); if (gu_likely(ann_size > 0)) { ann_size_t const tmp(gu::htog(ann_size)); ann_size_t off(sizeof(tmp)); ::memcpy(buf, &tmp, off); for (int i(0); i <= part_num && off < ann_size; ++i) { size_t const left(ann_size - off - 1); gu::byte_t const part_len (std::min(std::min(parts[i].len, left), max_part_len)); buf[off] = part_len; ++off; const gu::byte_t* const from( static_cast(parts[i].ptr)); std::copy(from, from + part_len, buf + off); off += part_len; } if (pad_size > 0) { ::memset(buf + off, 0, pad_size); off += pad_size; } assert (off == ann_size); } // log_info << "stored annotation of size: " << ann_size; return ann_size; } void KeySet::KeyPart::print_annotation(std::ostream& os, const gu::byte_t* buf) { ann_size_t const ann_size(gu::gtoh( *reinterpret_cast(buf))); size_t const begin(sizeof(ann_size_t)); size_t off(begin); while (off < ann_size) { if (off != begin) os << '/'; gu::byte_t const part_len(buf[off]); ++off; bool const last(ann_size == off + part_len); /* this is an attempt to guess whether we should interpret key part as * a string or numerical value */ bool const alpha(!last || part_len > 8); os << gu::Hexdump (buf + off, part_len, alpha); off += part_len; } } void KeySet::KeyPart::throw_buffer_too_short (size_t expected, size_t got) { #ifndef NDEBUG log_fatal #else gu_throw_error(EINVAL) #endif /* NDEBUG */ << "Buffer too short: expected " << expected << ", got " << got; assert(0); } void KeySet::KeyPart::throw_bad_type_version (wsrep_key_type_t t, int v) { #ifndef NDEBUG log_fatal #else gu_throw_error(EINVAL) #endif /* NDEBUG */ << "Internal program error: wsrep key type: " << t << ", writeset version: " << v; assert(0); } void KeySet::KeyPart::throw_bad_prefix (gu::byte_t p) { #ifndef NDEBUG log_fatal #else gu_throw_error(EPROTO) #endif /* NDEBUG */ << "Unsupported key prefix: " << int(p); assert(0); } void KeySet::KeyPart::throw_match_empty_key (Version my, Version other) { #ifndef NDEBUG log_fatal #else gu_throw_error(EINVAL) #endif /* NDEBUG */ << "Attempt to match against an empty key (" << my << ',' << other <<')'; assert(0); } void KeySet::KeyPart::print (std::ostream& os) const { Version const ver(version()); size_t const size(ver != EMPTY ? base_size(ver, data_, 1) : 0); os << '(' << prefix() << ',' << ver_str[ver] << ')' << gu::Hexdump(data_, size); if (annotated(ver)) { os << "="; print_annotation (os, data_ + size); } } /* returns true if left type is stronger than right */ static inline bool key_prefix_is_stronger_than(int const left, int const right) { return left > right; } KeySetOut::KeyPart::KeyPart (KeyParts& added, KeySetOut& store, const KeyPart* parent, const KeyData& kd, int const part_num, int const ws_ver, int const alignment) : hash_ (parent->hash_), part_ (0), value_(static_cast(kd.parts[part_num].ptr)), size_ (kd.parts[part_num].len), ver_ (parent->ver_), own_ (false) { assert (ver_); uint32_t const s(gu::htog(size_)); hash_.append (&s, sizeof(s)); hash_.append (value_, size_); KeySet::KeyPart::TmpStore ts; KeySet::KeyPart::HashData hd; hash_.gather(hd.buf); /* only leaf part of the key can be not of branch type */ bool const leaf (part_num + 1 == kd.parts_num); wsrep_key_type_t const type (leaf ? kd.type : KeyData::BRANCH_KEY_TYPE); int const prefix (KeySet::KeyPart::prefix(type, ws_ver)); // log_info << "Part " << part_num +1 << '/' << kd.parts_num << ": leaf: " << leaf << ", kd.type: " << kd.type << ", type: " << type << ", prefix: " << prefix; assert (kd.parts_num > part_num); KeySet::KeyPart kp(ts, hd, kd.parts, ver_, prefix, part_num, alignment); std::pair const inserted(added.insert(kp)); if (inserted.second) { /* The key part was successfully inserted, store it in the key set buffer */ inserted.first->store (store); } else { /* A matching key part instance is already present in the set, check constraints */ if (key_prefix_is_stronger_than(prefix, inserted.first->prefix())) { /* The key part instance present in the set has weaker constraint, store this instance as well and update inserted to point there. (we can't update already stored data - it was checksummed, so we have to store a duplicate with a stronger constraint) */ kp.store (store); inserted.first->update_ptr(kp.ptr()); /* It is a hack, but it should be safe to modify key part already inserted into unordered set, as long as modification does not change hash and equality test results. And we get it to point to a duplicate here.*/ } else if (leaf || key_prefix_is_stronger_than(inserted.first->prefix(), prefix)) { /* we don't throw DUPLICATE for branch parts, just ignore them. DUPLICATE is thrown only when the whole key is a duplicate. */ #ifndef NDEBUG if (leaf) log_debug << "KeyPart ctor: full duplicate of " << *inserted.first; else log_debug << "Duplicate of exclusive: " << *inserted.first; #endif throw DUPLICATE(); } } part_ = &(*inserted.first); } void KeySetOut::KeyPart::print (std::ostream& os) const { if (part_) os << *part_; else os << "0x0"; os << '(' << gu::Hexdump(value_, size_, true) << ')'; } /* Uncomment to enable KeySetOut::append() debug logging */ // #define GALERA_KSO_APPEND_DEBUG 1 #ifdef GALERA_KSO_APPEND_DEBUG #define KSO_APPEND_DEBUG(...) log_info << __VA_ARGS__ #else #define KSO_APPEND_DEBUG(...) #endif int KeySetOut::find_common_ancestor_with_previous(const KeyData& kd) const { int i(0); for (; i < kd.parts_num && size_t(i + 1) < prev_.size() && prev_[i + 1].match(kd.parts[i].ptr, kd.parts[i].len); ++i) { KSO_APPEND_DEBUG("prev[" << (i+1) << "]\n" << prev_[i+1] << "\nmatches\n" << gu::Hexdump(kd.parts[i].ptr, kd.parts[i].len, true)); } assert(size_t(i) < prev_.size()); return i; } size_t KeySetOut::append (const KeyData& kd) { int i = find_common_ancestor_with_previous(kd); KSO_APPEND_DEBUG("Append " << kd); /* if we have a fully matched key OR common ancestor is stronger, return */ if (i > 0) { int const kd_leaf_prefix(KeySet::KeyPart::prefix(kd.type, ws_ver_)); bool const common_ancestor_is_kd_leaf = (kd.parts_num == i); int const branch_prefix (KeySet::KeyPart::prefix(KeyData::BRANCH_KEY_TYPE, ws_ver_)); int const exclusive_prefix (KeySet::KeyPart::prefix(WSREP_KEY_EXCLUSIVE, ws_ver_)); int const common_ancestor_prefix = prev_[i].prefix(); bool const common_ancestor_is_prev_leaf = (prev_.size() == (i + 1U)); KSO_APPEND_DEBUG("Found common ancestor " << prev_[i] << " at position " << i); /* The common ancestor is already the strongest possible key. */ if (common_ancestor_prefix == exclusive_prefix) { KSO_APPEND_DEBUG("Common ancestor is exclusive"); return 0; } /* Common ancestor is leaf and is strong enough to cover both kd * leaf and branch. */ if (common_ancestor_is_prev_leaf && common_ancestor_prefix > kd_leaf_prefix && common_ancestor_prefix > branch_prefix) { KSO_APPEND_DEBUG("Common ancestor is previous leaf and stronger"); return 0; } if (common_ancestor_is_kd_leaf) { KSO_APPEND_DEBUG("Common ancestor is kd leaf"); if (kd_leaf_prefix <= common_ancestor_prefix) { KSO_APPEND_DEBUG("Common ancestor covers kd leaf"); return 0; } assert(common_ancestor_prefix <= kd_leaf_prefix); /* need to add a stronger copy of the leaf */ --i; } } int const anc(i); KSO_APPEND_DEBUG("Append key parts after ancestor " << i); const KeyPart* parent(&prev_[anc]); /* create parts that didn't match previous key and add to the set * of previously added keys. */ size_t const old_size (size()); int j(0); for (; i < kd.parts_num; ++i, ++j) { try { KeyPart kp(added_, *this, parent, kd, i, ws_ver_, alignment()); if (size_t(j) < new_.size()) { new_[j] = kp; } else { new_().push_back (kp); } parent = &new_[j]; } catch (KeyPart::DUPLICATE& e) { assert (i + 1 == kd.parts_num); /* There is a very small probability that child part throws DUPLICATE * even after parent was added as a new key. It does not matter: * a duplicate will be a duplicate in certification as well. */ goto out; } } assert (i == kd.parts_num); assert (anc + j == kd.parts_num); /* copy new parts to prev_ */ prev_().resize(1 + kd.parts_num); std::copy(new_().begin(), new_().begin() + j, prev_().begin() + anc + 1); /* acquire key part value if it is volatile */ if (kd.copy) for (int k(anc + 1); size_t(k) < prev_.size(); ++k) { prev_[k].acquire(); } out: return size() - old_size; } #undef KSO_APPEND_DEBUG } /* namespace galera */ galera-4-26.4.22/galera/src/galera_view.cpp000644 000162 177776 00000001062 14755062442 021524 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2015 Codership Oy // #include "galera_view.hpp" #include galera::View::View() : members_() { } galera::View::View(const wsrep_view_info_t& view_info) : members_() { for (int i(0); i < view_info.memb_num; ++i) { members_.insert(view_info.members[i].id); } } galera::View::~View() { } bool galera::View::subset_of(const MembSet& mset) const { return std::includes(mset.begin(), mset.end(), members_.begin(), members_.end(), UUIDCmp()); } galera-4-26.4.22/galera/src/replicator_str.cpp000644 000162 177776 00000140222 14755062442 022275 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2020 Codership Oy // #include "replicator_smm.hpp" #include "galera_info.hpp" #include "gcs_error.hpp" #include #include /* * Decide STR protocol version based on group protocol version. * For more infromation about versions, see table in replicator_smm.hpp. */ static int get_str_proto_ver(int const group_proto_ver) { switch (group_proto_ver) { case 1: return 0; case 2: case 3: case 4: case 5: return 1; case 6: case 7: case 8: case 9: // gcs intelligent donor selection. // include handling dangling comma in donor string. return 2; case 10: case 11: // 4.x // CC events in IST, certification index preload return 3; default: gu_throw_error(EPROTO) << "Can't find suitable STR protocol version based on " << "group protocol version: " << group_proto_ver; } } namespace galera { bool ReplicatorSMM::state_transfer_required(const wsrep_view_info_t& view_info, int const group_proto_ver, bool const rejoined) { const int str_proto_ver(get_str_proto_ver(group_proto_ver)); if (rejoined) { assert(view_info.view >= 0); if (state_uuid_ == view_info.state_id.uuid) // common history { wsrep_seqno_t const group_seqno(view_info.state_id.seqno); wsrep_seqno_t const local_seqno(last_committed()); if (str_proto_ver >= 3) return (local_seqno + 1 < group_seqno); // this CC will add 1 else return (local_seqno < group_seqno); } return true; } return false; } wsrep_status_t ReplicatorSMM::sst_received(const wsrep_gtid_t& state_id, const wsrep_buf_t* const state, int const rcode) { log_info << "SST received: " << state_id.uuid << ':' << state_id.seqno; gu::Lock lock(sst_mutex_); if (state_() != S_JOINING) { log_error << "not JOINING when sst_received() called, state: " << state_(); return WSREP_CONN_FAIL; } assert(rcode <= 0); if (rcode) { assert(state_id.seqno == WSREP_SEQNO_UNDEFINED); } sst_uuid_ = state_id.uuid; sst_seqno_ = rcode ? WSREP_SEQNO_UNDEFINED : state_id.seqno; assert(false == sst_received_); sst_received_ = true; sst_cond_.signal(); return WSREP_OK; } class StateRequest_v0 : public ReplicatorSMM::StateRequest { public: StateRequest_v0 (const void* const sst_req, ssize_t const sst_req_len) : req_(sst_req), len_(sst_req_len) {} ~StateRequest_v0 () {} virtual int version () const { return 0; } virtual const void* req () const { return req_; } virtual ssize_t len () const { return len_; } virtual const void* sst_req () const { return req_; } virtual ssize_t sst_len () const { return len_; } virtual const void* ist_req () const { return 0; } virtual ssize_t ist_len () const { return 0; } private: StateRequest_v0 (const StateRequest_v0&); StateRequest_v0& operator = (const StateRequest_v0&); const void* const req_; ssize_t const len_; }; class StateRequest_v1 : public ReplicatorSMM::StateRequest { public: static std::string const MAGIC; StateRequest_v1 (const void* sst_req, ssize_t sst_req_len, const void* ist_req, ssize_t ist_req_len); StateRequest_v1 (const void* str, ssize_t str_len); ~StateRequest_v1 () { if (own_ && req_) free (req_); } virtual int version () const { return 1; } virtual const void* req () const { return req_; } virtual ssize_t len () const { return len_; } virtual const void* sst_req () const { return req(sst_offset()); } virtual ssize_t sst_len () const { return len(sst_offset()); } virtual const void* ist_req () const { return req(ist_offset()); } virtual ssize_t ist_len () const { return len(ist_offset()); } private: StateRequest_v1 (const StateRequest_v1&); StateRequest_v1& operator = (const StateRequest_v1&); ssize_t sst_offset() const { return MAGIC.length() + 1; } ssize_t ist_offset() const { return sst_offset() + sizeof(uint32_t) + sst_len(); } ssize_t len (ssize_t offset) const { int32_t ret; gu::unserialize4(req_, offset, ret); return ret; } void* req (ssize_t offset) const { if (len(offset) > 0) return req_ + offset + sizeof(uint32_t); else return 0; } ssize_t const len_; char* const req_; bool const own_; }; std::string const StateRequest_v1::MAGIC("STRv1"); #ifndef INT32_MAX #define INT32_MAX 0x7fffffff #endif StateRequest_v1::StateRequest_v1 ( const void* const sst_req, ssize_t const sst_req_len, const void* const ist_req, ssize_t const ist_req_len) : len_(MAGIC.length() + 1 + sizeof(uint32_t) + sst_req_len + sizeof(uint32_t) + ist_req_len), req_(static_cast(malloc(len_))), own_(true) { if (!req_) gu_throw_error (ENOMEM) << "Could not allocate state request v1"; if (sst_req_len > INT32_MAX || sst_req_len < 0) gu_throw_error (EMSGSIZE) << "SST request length (" << sst_req_len << ") unrepresentable"; if (ist_req_len > INT32_MAX || ist_req_len < 0) gu_throw_error (EMSGSIZE) << "IST request length (" << sst_req_len << ") unrepresentable"; char* ptr(req_); strcpy (ptr, MAGIC.c_str()); ptr += MAGIC.length() + 1; ptr += gu::serialize4(uint32_t(sst_req_len), ptr, 0); memcpy (ptr, sst_req, sst_req_len); ptr += sst_req_len; ptr += gu::serialize4(uint32_t(ist_req_len), ptr, 0); memcpy (ptr, ist_req, ist_req_len); assert ((ptr - req_) == (len_ - ist_req_len)); } // takes ownership over str buffer StateRequest_v1::StateRequest_v1 (const void* str, ssize_t str_len) : len_(str_len), req_(static_cast(const_cast(str))), own_(false) { if (sst_offset() + 2*sizeof(uint32_t) > size_t(len_)) { assert(0); gu_throw_error (EINVAL) << "State transfer request is too short: " << len_ << ", must be at least: " << (sst_offset() + 2*sizeof(uint32_t)); } if (strncmp (req_, MAGIC.c_str(), MAGIC.length())) { assert(0); gu_throw_error (EINVAL) << "Wrong magic signature in state request v1."; } if (sst_offset() + sst_len() + 2*sizeof(uint32_t) > size_t(len_)) { gu_throw_error (EINVAL) << "Malformed state request v1: sst length: " << sst_len() << ", total length: " << len_; } if (ist_offset() + ist_len() + sizeof(uint32_t) != size_t(len_)) { gu_throw_error (EINVAL) << "Malformed state request v1: parsed field " "length " << sst_len() << " is not equal to total request length " << len_; } } static ReplicatorSMM::StateRequest* read_state_request (const void* const req, size_t const req_len) { const char* const str(static_cast(req)); bool const v1(req_len > StateRequest_v1::MAGIC.length() && !strncmp(str, StateRequest_v1::MAGIC.c_str(), StateRequest_v1::MAGIC.length())); log_info << "Detected STR version: " << int(v1) << ", req_len: " << req_len << ", req: " << str; if (v1) { return (new StateRequest_v1(req, req_len)); } else { return (new StateRequest_v0(req, req_len)); } } class IST_request { public: IST_request() : peer_(), uuid_(), last_applied_(), group_seqno_() { } IST_request(const std::string& peer, const wsrep_uuid_t& uuid, wsrep_seqno_t last_applied, wsrep_seqno_t last_missing_seqno) : peer_(peer), uuid_(uuid), last_applied_(last_applied), group_seqno_(last_missing_seqno) { } const std::string& peer() const { return peer_ ; } const wsrep_uuid_t& uuid() const { return uuid_ ; } wsrep_seqno_t last_applied() const { return last_applied_; } wsrep_seqno_t group_seqno() const { return group_seqno_; } private: friend std::ostream& operator<<(std::ostream&, const IST_request&); friend std::istream& operator>>(std::istream&, IST_request&); std::string peer_; wsrep_uuid_t uuid_; wsrep_seqno_t last_applied_; wsrep_seqno_t group_seqno_; }; std::ostream& operator<<(std::ostream& os, const IST_request& istr) { return (os << istr.uuid_ << ":" << istr.last_applied_ << "-" << istr.group_seqno_ << "|" << istr.peer_); } std::istream& operator>>(std::istream& is, IST_request& istr) { char c; return (is >> istr.uuid_ >> c >> istr.last_applied_ >> c >> istr.group_seqno_ >> c >> istr.peer_); } static void get_ist_request(const ReplicatorSMM::StateRequest* str, IST_request* istr) { assert(str->ist_len()); std::string ist_str(static_cast(str->ist_req()), str->ist_len()); std::istringstream is(ist_str); is >> *istr; } static bool sst_is_trivial (const void* const req, size_t const len) { /* Check that the first string in request == ReplicatorSMM::TRIVIAL_SST */ static size_t const trivial_len(strlen(ReplicatorSMM::TRIVIAL_SST) + 1); return (len >= trivial_len && !::memcmp(req, ReplicatorSMM::TRIVIAL_SST, trivial_len)); } static bool no_sst (const void* const req, size_t const len) { /* Check that the first string in request == ReplicatorSMM::NO_SST */ static size_t const no_len(strlen(ReplicatorSMM::NO_SST) + 1); return (len >= no_len && !::memcmp(req, ReplicatorSMM::NO_SST, no_len)); } wsrep_seqno_t ReplicatorSMM::donate_sst(void* const recv_ctx, const StateRequest& streq, const wsrep_gtid_t& state_id, bool const bypass) { wsrep_buf_t const str = { streq.sst_req(), size_t(streq.sst_len()) }; wsrep_cb_status const err(sst_donate_cb_(app_ctx_, recv_ctx, &str, &state_id, NULL, bypass)); wsrep_seqno_t const ret (WSREP_CB_SUCCESS == err ? state_id.seqno : -ECANCELED); if (ret < 0) { log_error << "SST " << (bypass ? "bypass " : "") << "failed: " << err; } return ret; } struct slg { gcache::GCache& gcache_; bool unlock_; slg(gcache::GCache& cache) : gcache_(cache), unlock_(false){} ~slg() { if (unlock_) gcache_.seqno_unlock(); } }; static wsrep_seqno_t run_ist_senders(ist::AsyncSenderMap& ist_senders, const gu::Config& config, const std::string& peer, wsrep_seqno_t const preload_start, wsrep_seqno_t const cc_seqno, wsrep_seqno_t const cc_lowest, int const proto_ver, slg& seqno_lock_guard, wsrep_seqno_t const rcode) { try { ist_senders.run(config, peer, preload_start, cc_seqno, cc_lowest, proto_ver); // seqno will be unlocked when sender exists seqno_lock_guard.unlock_ = false; return rcode; } catch (gu::Exception& e) { log_warn << "IST failed: " << e.what(); return -e.get_errno(); } } void ReplicatorSMM::process_state_req(void* recv_ctx, const void* req, size_t req_size, wsrep_seqno_t const seqno_l, wsrep_seqno_t const donor_seq) { assert(recv_ctx != 0); assert(seqno_l > -1); assert(req != 0); StateRequest* const streq(read_state_request(req, req_size)); // Guess correct STR protocol version. Here we assume that the // replicator protocol version didn't change between sending // and receiving STR message. Unfortunately the protocol version // is not yet available in STR message. int const str_proto_ver(get_str_proto_ver(protocol_version_)); LocalOrder lo(seqno_l); gu_trace(local_monitor_.enter(lo)); apply_monitor_.drain(donor_seq); if (co_mode_ != CommitOrder::BYPASS) commit_monitor_.drain(donor_seq); state_.shift_to(S_DONOR); // somehow the following does not work, string is initialized beyond // the first \0: //std::string const req_str(static_cast(streq->sst_req()), // streq->sst_len()); // have to resort to C ways. char* const tmp(strndup(static_cast(streq->sst_req()), streq->sst_len())); std::string const req_str(tmp); free (tmp); bool const trivial_sst(sst_is_trivial(streq->sst_req(), streq->sst_len())); bool const skip_sst(trivial_sst || no_sst(streq->sst_req(), streq->sst_len())); wsrep_seqno_t rcode (0); bool join_now = true; if (not skip_sst) { slg seqno_lock_guard(gcache_); if (streq->ist_len()) { IST_request istr; get_ist_request(streq, &istr); if (istr.uuid() == state_uuid_ && istr.last_applied() >= 0) { log_info << "IST request: " << istr; wsrep_seqno_t const first ((str_proto_ver < 3 || cc_lowest_trx_seqno_ == 0) ? istr.last_applied() + 1 : std::min(cc_lowest_trx_seqno_, istr.last_applied()+1)); try { gcache_.seqno_lock(first); seqno_lock_guard.unlock_ = true; } catch(gu::NotFound& nf) { log_info << "IST first seqno " << istr.last_applied() + 1 << " not found from cache, falling back to SST"; // @todo: close IST channel explicitly goto full_sst; } if (streq->sst_len()) // if joiner is waiting for SST, notify it { wsrep_gtid_t const state_id = { istr.uuid(), istr.last_applied() }; gu_trace(rcode = donate_sst(recv_ctx, *streq, state_id, true)); // we will join in sst_sent. join_now = false; } if (rcode >= 0) { rcode = run_ist_senders(ist_senders_, config_, istr.peer(), first, cc_seqno_, cc_lowest_trx_seqno_, /* Historically IST messages are versioned * with the global replicator protocol. * Need to keep it that way for backward * compatibility */ protocol_version_, seqno_lock_guard, rcode); } else { log_error << "Failed to bypass SST"; } goto out; } } full_sst: assert(!seqno_lock_guard.unlock_); if (cert_.nbo_size() > 0) { log_warn << "Non-blocking operation in progress, cannot donate SST"; rcode = -EAGAIN; } else if (streq->sst_len()) { assert(0 == rcode); wsrep_gtid_t const state_id = { state_uuid_, donor_seq }; if (str_proto_ver >= 3) { if (streq->version() > 0) { if (streq->ist_len() <= 0) { if (not trivial_sst) { log_warn << "Joiner didn't provide IST connection " "info - cert. index preload impossible, bailing " "out."; rcode = -ENOMSG; } else { /* don't warn about trivial SST requests: such nodes * are not supposed to fully join the cluster, e,g, * garbd */ } goto out; } wsrep_seqno_t preload_start(cc_lowest_trx_seqno_); try { if (preload_start <= 0) { preload_start = cc_seqno_; } gcache_.seqno_lock(preload_start); seqno_lock_guard.unlock_ = true; } catch (gu::NotFound& nf) { log_warn << "Cert index preload first seqno " << preload_start << " not found from gcache (min available: " << gcache_.seqno_min() << ')'; rcode = -ENOMSG; goto out; } log_info << "Cert index preload: " << preload_start << " -> " << cc_seqno_; IST_request istr; get_ist_request(streq, &istr); // Send trxs to rebuild cert index. rcode = run_ist_senders(ist_senders_, config_, istr.peer(), preload_start, cc_seqno_, preload_start, /* Historically IST messages are versioned * with the global replicator protocol. * Need to keep it that way for backward * compatibility */ protocol_version_, seqno_lock_guard, rcode); if (rcode < 0) goto out; } else /* streq->version() == 0 */ { log_info << "STR v0: assuming backup request, skipping " "cert. index preload."; } } rcode = donate_sst(recv_ctx, *streq, state_id, false); // we will join in sst_sent. join_now = false; } else { log_warn << "SST request is null, SST canceled."; rcode = -ECANCELED; } } out: delete streq; local_monitor_.leave(lo); if (join_now || rcode < 0) { gcs_.join(gu::GTID(state_uuid_, donor_seq), rcode); } } void ReplicatorSMM::prepare_for_IST (void*& ptr, ssize_t& len, int const group_proto_ver, int const str_proto_ver, const wsrep_uuid_t& group_uuid, wsrep_seqno_t const last_needed) { assert(group_uuid != GU_UUID_NIL); // Up from STR protocol version 3 joiner is assumed to be able receive // some transactions to rebuild cert index, so IST receiver must be // prepared regardless of the group. wsrep_seqno_t last_applied(last_committed()); ist_event_queue_.reset(); if (state_uuid_ != group_uuid) { if (str_proto_ver < 3) { gu_throw_error (EPERM) << "Local state UUID (" << state_uuid_ << ") does not match group state UUID (" << group_uuid << ')'; } else { last_applied = -1; // to cause full SST } } else { assert(last_applied < last_needed); } if (last_applied < 0 && str_proto_ver < 3) { gu_throw_error (EPERM) << "Local state seqno is undefined"; } wsrep_seqno_t const first_needed(last_applied + 1); log_info << "####### IST uuid:" << state_uuid_ << " f: " << first_needed << ", l: " << last_needed << ", STRv: " << str_proto_ver; //remove /* Historically IST messages are versioned with the global replicator * protocol. Need to keep it that way for backward compatibility */ std::string recv_addr(ist_receiver_.prepare(first_needed, last_needed, group_proto_ver, source_id())); std::ostringstream os; /* NOTE: in case last_applied is -1, first_needed is 0, but first legal * cached seqno is 1 so donor will revert to SST anyways, as is required */ os << IST_request(recv_addr, state_uuid_, last_applied, last_needed); char* str = strdup (os.str().c_str()); // cppcheck-suppress nullPointer if (!str) gu_throw_error (ENOMEM) << "Failed to allocate IST buffer."; log_debug << "Prepared IST request: " << str; len = strlen(str) + 1; ptr = str; } ReplicatorSMM::StateRequest* ReplicatorSMM::prepare_state_request (const void* sst_req, ssize_t sst_req_len, int const group_proto_ver, int const str_proto_ver, const wsrep_uuid_t& group_uuid, wsrep_seqno_t const last_needed_seqno) { try { // IF there are ongoing NBO, SST might not be possible because // ongoing NBO is blocking and waiting for NBO end events. // Therefore in precense of ongoing NBOs we set SST request // string to zero and hope that donor can serve IST. size_t const nbo_size(cert_.nbo_size()); if (nbo_size) { log_info << "Non-blocking operation is ongoing. " "Node can receive IST only."; sst_req = NULL; sst_req_len = 0; } switch (str_proto_ver) { case 0: if (0 == sst_req_len) gu_throw_error(EPERM) << "SST is not possible."; return new StateRequest_v0 (sst_req, sst_req_len); case 1: case 2: case 3: { void* ist_req(0); ssize_t ist_req_len(0); try { // Note: IST uses group protocol version. gu_trace(prepare_for_IST (ist_req, ist_req_len, group_proto_ver, str_proto_ver, group_uuid, last_needed_seqno)); assert(ist_req_len > 0); assert(NULL != ist_req); } catch (gu::Exception& e) { log_warn << "Failed to prepare for incremental state transfer: " << e.what() << ". IST will be unavailable."; if (0 == sst_req_len) gu_throw_error(EPERM) << "neither SST nor IST is possible."; } StateRequest* ret = new StateRequest_v1 (sst_req, sst_req_len, ist_req, ist_req_len); free (ist_req); return ret; } default: gu_throw_fatal << "Unsupported STR protocol: " << str_proto_ver; } } catch (std::exception& e) { log_fatal << "State Transfer Request preparation failed: " << e.what() << " Can't continue, aborting."; } catch (...) { log_fatal << "State Transfer Request preparation failed: " "unknown exception. Can't continue, aborting."; } abort(); } static bool retry_str(int ret) { return (ret == -EAGAIN || ret == -ENOTCONN); } void ReplicatorSMM::send_state_request (const StateRequest* const req, int const str_proto_ver) { long ret; long tries = 0; gu_uuid_t ist_uuid = {{0, }}; gcs_seqno_t ist_seqno = GCS_SEQNO_ILL; if (req->ist_len()) { IST_request istr; get_ist_request(req, &istr); ist_uuid = istr.uuid(); ist_seqno = istr.last_applied(); } do { tries++; gcs_seqno_t seqno_l; ret = gcs_.request_state_transfer(str_proto_ver, req->req(), req->len(), sst_donor_, gu::GTID(ist_uuid, ist_seqno),seqno_l); if (ret < 0) { if (!retry_str(ret)) { log_error << "Requesting state transfer failed: " << gcs_state_transfer_error_str(-ret); } else if (1 == tries) { log_info << "Requesting state transfer failed: " << gcs_state_transfer_error_str(-ret) << ". " << "Will keep retrying every " << sst_retry_sec_ << " second(s)"; } } if (seqno_l != GCS_SEQNO_ILL) { /* Check that we're not running out of space in monitor. */ if (local_monitor_.would_block(seqno_l)) { log_error << "Slave queue grew too long while trying to " << "request state transfer " << tries << " time(s). " << "Please make sure that there is " << "at least one fully synced member in the group. " << "Application must be restarted."; ret = -EDEADLK; } else { // we are already holding local monitor LocalOrder lo(seqno_l); local_monitor_.self_cancel(lo); } } } while (retry_str(ret) && (usleep(sst_retry_sec_ * 1000000), true)); if (ret >= 0) { if (1 == tries) { log_info << "Requesting state transfer: success, donor: " << ret; } else { log_info << "Requesting state transfer: success after " << tries << " tries, donor: " << ret; } } else { sst_state_ = SST_REQ_FAILED; st_.set(state_uuid_, last_committed(), safe_to_bootstrap_); st_.mark_safe(); gu::Lock lock(closing_mutex_); if (!closing_ && state_() > S_CLOSED) { log_fatal << "State transfer request failed unrecoverably: " << gcs_state_transfer_error_str(-ret) << ". Most likely " << "it is due to inability to communicate with the " << "cluster primary component. Restart required."; abort(); } else { // connection is being closed, send failure is expected } } } void ReplicatorSMM::request_state_transfer (void* recv_ctx, int const group_proto_ver, const wsrep_uuid_t& group_uuid, wsrep_seqno_t const cc_seqno, const void* const sst_req, ssize_t const sst_req_len) { assert(sst_req_len >= 0); int const str_proto_ver(get_str_proto_ver(group_proto_ver)); StateRequest* const req(prepare_state_request(sst_req, sst_req_len, group_proto_ver, str_proto_ver, group_uuid, cc_seqno)); gu::Lock sst_lock(sst_mutex_); sst_received_ = false; st_.mark_unsafe(); GU_DBUG_SYNC_WAIT("before_send_state_request"); send_state_request(req, str_proto_ver); state_.shift_to(S_JOINING); sst_state_ = SST_WAIT; sst_seqno_ = WSREP_SEQNO_UNDEFINED; GU_DBUG_SYNC_WAIT("after_shift_to_joining"); /* There are two places where we may need to adjust GCache. * This is the first one, which we can do while waiting for SST to complete. * Here we reset seqno map completely if we have different histories. * This MUST be done before IST starts. */ bool const first_reset (state_uuid_ /* GCache has */ != group_uuid /* current PC has */); if (first_reset) { log_info << "Resetting GCache seqno map due to different histories."; gcache_.seqno_reset(gu::GTID(group_uuid, cc_seqno)); } if (sst_req_len != 0) { if (sst_is_trivial(sst_req, sst_req_len) || no_sst (sst_req, sst_req_len)) { sst_uuid_ = group_uuid; sst_seqno_ = cc_seqno; sst_received_ = true; } else { while (false == sst_received_) sst_lock.wait(sst_cond_); } if (sst_uuid_ != group_uuid) { log_fatal << "Application received wrong state: " << "\n\tReceived: " << sst_uuid_ << "\n\tRequired: " << group_uuid; sst_state_ = SST_FAILED; log_fatal << "Application state transfer failed. This is " << "unrecoverable condition, restart required."; st_.set(sst_uuid_, sst_seqno_, safe_to_bootstrap_); st_.mark_safe(); abort(); } else { assert(sst_seqno_ != WSREP_SEQNO_UNDEFINED); /* There are two places where we may need to adjust GCache. * This is the second one. * Here we reset seqno map completely if we have gap in seqnos * between the received snapshot and current GCache contents. * This MUST be done before IST starts. */ // there may be possible optimization to this when cert index // transfer is implemented (it may close the gap), but not by much. if (!first_reset && (last_committed() /* GCache has */ != sst_seqno_ /* current state has */)) { log_info << "Resetting GCache seqno map due to seqno gap: " << last_committed() << ".." << sst_seqno_; gcache_.seqno_reset(gu::GTID(sst_uuid_, sst_seqno_)); } update_state_uuid (sst_uuid_); if (group_proto_ver < PROTO_VER_GALERA_3_MAX) { log_error << "Rolling upgrade from group protocol version " << "earlier than " << PROTO_VER_GALERA_3_MAX << " is not supported. Please upgrade " << "Galera library to latest in Galera 3 series on " << "all of the nodes in the cluster before " << "continuing."; abort(); } else if (group_proto_ver == PROTO_VER_GALERA_3_MAX) { // Rolling upgrade from Galera 3 PROTO_VER_GALERA_3_MAX. gu::GTID const cert_position (sst_uuid_, std::max(cc_seqno, sst_seqno_)); cert_.assign_initial_position( cert_position, std::get<0>(get_trx_protocol_versions(group_proto_ver))); // with higher versions this happens in cert index preload } apply_monitor_.set_initial_position(WSREP_UUID_UNDEFINED, -1); apply_monitor_.set_initial_position(sst_uuid_, sst_seqno_); if (co_mode_ != CommitOrder::BYPASS) { commit_monitor_.set_initial_position(WSREP_UUID_UNDEFINED, -1); commit_monitor_.set_initial_position(sst_uuid_, sst_seqno_); } log_info << "Installed new state from SST: " << state_uuid_ << ":" << sst_seqno_; } } else { assert (state_uuid_ == group_uuid); sst_seqno_ = last_committed(); } if (st_.corrupt()) { if (sst_req_len != 0 && !sst_is_trivial(sst_req, sst_req_len)) { // Note: not storing sst seqno in state file to avoid // recovering to incorrect state if the node is // killed during IST. st_.mark_uncorrupt(sst_uuid_, WSREP_SEQNO_UNDEFINED); } else { log_fatal << "Application state is corrupt and cannot " << "be recovered. Restart required."; abort(); } } else { // Clear seqno from state file. Otherwise if node gets killed // during IST, it may recover to incorrect position. st_.set(state_uuid_, WSREP_SEQNO_UNDEFINED, safe_to_bootstrap_); st_.mark_safe(); } if (req->ist_len() > 0) { if (state_uuid_ != group_uuid) { log_fatal << "Sanity check failed: my state UUID " << state_uuid_ << " is different from group state UUID " << group_uuid << ". Can't continue with IST. Aborting."; st_.set(state_uuid_, last_committed(), safe_to_bootstrap_); st_.mark_safe(); abort(); } // IST is prepared only with str proto ver 1 and above // IST is *always* prepared at str proto ver 3 or higher if (last_committed() < cc_seqno || str_proto_ver >= 3) { wsrep_seqno_t const ist_from(last_committed() + 1); wsrep_seqno_t const ist_to(cc_seqno); bool const do_ist(ist_from > 0 && ist_from <= ist_to); if (do_ist) { log_info << "Receiving IST: " << (ist_to - ist_from + 1) << " writesets, seqnos " << ist_from << "-" << ist_to; } else { log_info << "Cert. index preload up to " << ist_from - 1; } ist_receiver_.ready(ist_from); recv_IST(recv_ctx); wsrep_seqno_t const ist_seqno(ist_receiver_.finished()); if (do_ist) { assert(ist_seqno > sst_seqno_); // must exceed sst_seqno_ sst_seqno_ = ist_seqno; // Note: apply_monitor_ must be drained to avoid race between // IST appliers and GCS appliers, GCS action source may // provide actions that have already been applied via IST. log_info << "Draining apply monitors after IST up to " << sst_seqno_; apply_monitor_.drain(sst_seqno_); set_initial_position(group_uuid, sst_seqno_); } else { assert(sst_seqno_ > 0); // must have been esptablished via SST assert(ist_seqno >= cc_seqno); // index must be rebuilt up to assert(ist_seqno <= sst_seqno_); } if (ist_seqno == sst_seqno_) { log_info << "IST received: " << state_uuid_ << ":" <= cc_seqno); } #ifndef NDEBUG { gu::Lock lock(closing_mutex_); assert(sst_seqno_ >= cc_seqno || closing_ || state_() == S_CLOSED); } #endif /* NDEBUG */ delete req; } void ReplicatorSMM::process_IST_writeset(void* recv_ctx, const TrxHandleSlavePtr& ts_ptr) { TrxHandleSlave& ts(*ts_ptr); assert(ts.global_seqno() > 0); assert(ts.state() != TrxHandle::S_COMMITTED); assert(ts.state() != TrxHandle::S_ROLLED_BACK); bool const skip(ts.is_dummy()); if (gu_likely(!skip)) { ts.verify_checksum(); assert(ts.certified()); assert(ts.depends_seqno() >= 0); } else { assert(ts.is_dummy()); } try { apply_trx(recv_ctx, ts); } catch (...) { st_.mark_corrupt(); throw; } GU_DBUG_SYNC_WAIT("recv_IST_after_apply_trx"); if (gu_unlikely (gu::Logger::no_log(gu::LOG_DEBUG) == false)) { std::ostringstream os; if (gu_likely(!skip)) os << "IST received trx body: " << ts; else os << "IST skipping trx " << ts.global_seqno(); log_debug << os.str(); } } void ReplicatorSMM::recv_IST(void* recv_ctx) { ISTEvent::Type event_type(ISTEvent::T_NULL); TrxHandleSlavePtr ts; wsrep_view_info_t* view; try { bool exit_loop(false); while (exit_loop == false) { ISTEvent ev(ist_event_queue_.pop_front()); event_type = ev.type(); switch (event_type) { case ISTEvent::T_NULL: exit_loop = true; continue; case ISTEvent::T_TRX: ts = ev.ts(); assert(ts); process_IST_writeset(recv_ctx, ts); exit_loop = ts->exit_loop(); continue; case ISTEvent::T_VIEW: { view = ev.view(); wsrep_seqno_t const cs(view->state_id.seqno); submit_view_info(recv_ctx, view); ::free(view); CommitOrder co(cs, CommitOrder::NO_OOOC); commit_monitor_.leave(co); ApplyOrder ao(cs, cs - 1, false); apply_monitor_.leave(ao); GU_DBUG_SYNC_WAIT("recv_IST_after_conf_change"); continue; } } gu_throw_fatal << "Unrecognized event of type " << ev.type(); } } catch (gu::Exception& e) { std::ostringstream os; os << "Receiving IST failed, node restart required: " << e.what(); switch (event_type) { case ISTEvent::T_NULL: os << ". Null event."; break; case ISTEvent::T_TRX: if (ts) os << ". Failed writeset: " << *ts; else os << ". Corrupt IST event queue."; break; case ISTEvent::T_VIEW: os << ". VIEW event"; break; } log_fatal << os.str(); gu::Lock lock(closing_mutex_); start_closing(); } } void ReplicatorSMM::handle_ist_nbo(const TrxHandleSlavePtr& ts, bool must_apply, bool preload) { if (must_apply) { ts->verify_checksum(); Certification::TestResult result(cert_.append_trx(ts)); switch (result) { case Certification::TEST_OK: if (ts->nbo_end()) { // This is the same as in process_trx() if (ts->ends_nbo() == WSREP_SEQNO_UNDEFINED) { assert(ts->is_dummy()); } else { // Signal NBO waiter gu::shared_ptr::type nbo_ctx( cert_.nbo_ctx(ts->ends_nbo())); assert(nbo_ctx != 0); nbo_ctx->set_ts(ts); return; // not pushing to queue below } } break; case Certification::TEST_FAILED: { assert(ts->nbo_end()); // non-effective nbo_end assert(ts->is_dummy()); break; } } /* regardless of certification outcome, event must be passed to * apply_trx() as it carries global seqno */ } else { // Skipping NBO events in preload is fine since the joiner either // has all the events applied in case of pure IST or the // donor refuses to donate SST from the position with active NBO. assert(preload); log_debug << "Skipping NBO event: " << ts; cert_.append_dummy_preload(ts); assert(ts->global_seqno() == cert_.position()); } if (gu_likely(must_apply == true)) { ist_event_queue_.push_back(ts); } } // Append IST trx to certification index. As trx has passed certification // on donor, certification is expected to pass. If it fails, exception // is thrown as the state is unrecoverable. static void append_ist_trx(galera::Certification& cert, const TrxHandleSlavePtr& ts) { Certification::TestResult result(cert.append_trx(ts)); if (result != Certification::TEST_OK) { gu_throw_fatal << "Pre IST trx append returned unexpected " << "certification result " << result << ", expected " << Certification::TEST_OK << "must abort to maintain consistency, " << " cert position: " << cert.position() << " ts: " << *ts; } } void ReplicatorSMM::handle_ist_trx_preload(const TrxHandleSlavePtr& ts, bool const must_apply) { if (not ts->is_dummy()) { append_ist_trx(cert_, ts); if (not must_apply) { // Pure preload event will not get applied, so mark it committed // here for certification bookkeeping. cert_.set_trx_committed(*ts); } } else if (cert_.position() != WSREP_SEQNO_UNDEFINED) { // Append dummy trx to keep certification trx map continuous which // is a requirement for cert purge to work properly. cert_.append_dummy_preload(ts); assert(ts->global_seqno() == cert_.position()); } } void ReplicatorSMM::handle_ist_trx(const TrxHandleSlavePtr& ts, bool must_apply, bool preload) { if (preload) { handle_ist_trx_preload(ts, must_apply); } if (must_apply) { ist_event_queue_.push_back(ts); } } void ReplicatorSMM::ist_trx(const TrxHandleSlavePtr& ts, bool must_apply, bool preload) { assert(ts != 0); assert(ts->depends_seqno() >= 0 || ts->is_dummy() || ts->nbo_end()); assert(ts->local_seqno() == WSREP_SEQNO_UNDEFINED); assert(sst_seqno_ > 0); ts->verify_checksum(); // Note: Write sets which do not have preload or must_apply flag set // are used to populate joiner gcache to have enough history // to be able to donate IST for following joiners. Therefore // they don't need to be applied or used to populate certification // index and are just skipped here. if (not (preload || must_apply)) { return; } if (gu_unlikely(cert_.position() == WSREP_SEQNO_UNDEFINED)) { if (not ts->is_dummy()) { // This is the first pre IST event for rebuilding cert index. // Note that we skip dummy write sets here as they don't carry // version information. If the IST will contain only dummy // write sets, the last CC belonging into IST which corresponds // to CC which triggered STR, will initialize cert index. assert(ts->version() > 0); cert_.assign_initial_position( /* proper UUID will be installed by CC */ gu::GTID(gu::UUID(), ts->global_seqno() - 1), ts->version()); } } assert(ts->state() == TrxHandleSlave::S_REPLICATING); ts->set_state(TrxHandleSlave::S_CERTIFYING); if (ts->nbo_start() || ts->nbo_end()) { handle_ist_nbo(ts, must_apply, preload); } else { handle_ist_trx(ts, must_apply, preload); } } void ReplicatorSMM::ist_end(const ist::Result& result) { ist_event_queue_.eof(result); } void galera::ReplicatorSMM::process_ist_conf_change(const gcs_act_cchange& conf) { // IST should contain only ordered CCs assert(conf.repl_proto_ver >= PROTO_VER_ORDERED_CC); // Drain monitors to make sure that all preceding IST events have // been applied. drain_monitors(conf.seqno - 1); // Create view info. This will be consumed by ist_event_queue_.push_back(). wsrep_uuid_t uuid_undefined(WSREP_UUID_UNDEFINED); wsrep_view_info_t* const view_info (galera_view_info_create(conf, capabilities(conf.repl_proto_ver), -1, uuid_undefined)); // IST view status should always be Primary assert(view_info->status == WSREP_VIEW_PRIMARY); // Establish protocol version before adjusting cert position as // trx_params_.version is decided there. establish_protocol_versions (conf.repl_proto_ver); cert_.adjust_position(*view_info, gu::GTID(conf.uuid, conf.seqno), trx_params_.version_); update_incoming_list(*view_info); record_cc_seqnos(conf.seqno, "ist"); // TO monitors need to be entered here to maintain critical // section over passing the view through the event queue to // an applier and ensure that the view is submitted in isolation. // Applier is to leave monitors and free the view after it is // submitted. ApplyOrder ao(conf.seqno, conf.seqno - 1, false); apply_monitor_.enter(ao); CommitOrder co(conf.seqno, CommitOrder::NO_OOOC); commit_monitor_.enter(co); ist_event_queue_.push_back(view_info); } void ReplicatorSMM::ist_cc(const gcs_action& act, bool must_apply, bool preload) { assert(GCS_ACT_CCHANGE == act.type); assert(act.seqno_g > 0); gcs_act_cchange const conf(act.buf, act.size); assert(conf.conf_id >= 0); // Primary configuration assert(conf.seqno == act.seqno_g); if (gu_unlikely(cert_.position() == WSREP_SEQNO_UNDEFINED) && (must_apply || preload)) { // This is the first IST event for rebuilding cert index, // need to initialize certification establish_protocol_versions(conf.repl_proto_ver); cert_.assign_initial_position(gu::GTID(conf.uuid, conf.seqno - 1), trx_params_.version_); } if (must_apply == true) { // Will generate and queue view info. Monitors are handled by // slave appliers when the view_info is consumed. process_ist_conf_change(conf); } else { if (preload == true) { wsrep_uuid_t uuid_undefined(WSREP_UUID_UNDEFINED); wsrep_view_info_t* const view_info( galera_view_info_create(conf, capabilities(conf.repl_proto_ver), -1, uuid_undefined)); /* CC is part of index preload but won't be processed * by process_conf_change() * Order of these calls is essential: trx_params_.version_ may * be altered by establish_protocol_versions() */ establish_protocol_versions(conf.repl_proto_ver); cert_.adjust_position(*view_info, gu::GTID(conf.uuid, conf.seqno), trx_params_.version_); // record CC related state seqnos, needed for IST on DONOR record_cc_seqnos(conf.seqno, "preload"); ::free(view_info); } } } } /* namespace galera */ galera-4-26.4.22/galera/src/data_set.hpp000644 000162 177776 00000011237 14755062442 021035 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2013 Codership Oy // #ifndef GALERA_DATA_SET_HPP #define GALERA_DATA_SET_HPP #include "gu_rset.hpp" #include "gu_vlq.hpp" namespace galera { class DataSet { public: enum Version { EMPTY = 0, VER1 }; static Version const MAX_VERSION = VER1; static Version version (unsigned int ver) { if (gu_likely (ver <= MAX_VERSION)) return static_cast(ver); gu_throw_error (EINVAL) << "Unrecognized DataSet version: " << ver; } /*! Dummy class to instantiate DataSetOut */ class RecordOut {}; /*! A class to instantiate DataSetIn: provides methods necessary to * iterate over the records serialized into single input buffer */ class RecordIn { public: static size_t serial_size (const gu::byte_t* const buf, size_t const size) { /* There's a single record in a dataset */ return size; } size_t serial_size () const { return size_; } RecordIn (const gu::byte_t* buf, size_t size) : size_(size), buf_(buf) {} gu::Buf buf() { gu::Buf ret = { buf_, size_ }; return ret; } private: ssize_t size_; const gu::byte_t* buf_; }; /* class RecordIn */ }; /* class DataSet */ #if defined(__GNUG__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4) # pragma GCC diagnostic push # endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4) # pragma GCC diagnostic ignored "-Weffc++" #endif class DataSetOut : public gu::RecordSetOut { public: DataSetOut () // empty ctor for slave TrxHandle : gu::RecordSetOut(), version_() {} DataSetOut (gu::byte_t* reserved, size_t reserved_size, const BaseName& base_name, DataSet::Version version, gu::RecordSet::Version rsv) : gu::RecordSetOut ( reserved, reserved_size, base_name, check_type(version), rsv ), version_(version) { assert((uintptr_t(reserved) % GU_WORD_BYTES) == 0); } size_t append (const void* const src, size_t const size, bool const store) { /* append data as is, don't count as a new record */ gu_trace( gu::RecordSetOut::append (src, size, store, false); ); /* this will be deserialized using DataSet::RecordIn in DataSetIn */ return size; } DataSet::Version version () const { return count() ? version_ : DataSet::EMPTY; } typedef gu::RecordSet::GatherVector GatherVector; private: // depending on version we may pack data differently DataSet::Version const version_; static gu::RecordSet::CheckType check_type (DataSet::Version ver) { switch (ver) { case DataSet::EMPTY: break; /* Can't create EMPTY DataSetOut */ case DataSet::VER1: return gu::RecordSet::CHECK_MMH128; } throw; } }; /* class DataSetOut */ class DataSetIn : public gu::RecordSetIn { public: DataSetIn (DataSet::Version ver, const gu::byte_t* buf, size_t size) : gu::RecordSetIn(buf, size, false), version_(ver) {} DataSetIn () : gu::RecordSetIn(), version_(DataSet::EMPTY) {} void init (DataSet::Version ver, const gu::byte_t* buf, size_t size) { gu::RecordSetIn::init(buf, size, false); version_ = ver; } gu::Buf next () const { return gu::RecordSetIn::next().buf(); } private: DataSet::Version version_; }; /* class DataSetIn */ #if defined(__GNUG__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4) # pragma GCC diagnostic pop # endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4) #endif } /* namespace galera */ #endif // GALERA_DATA_SET_HPP galera-4-26.4.22/galera/src/gcs_dummy.cpp000644 000162 177776 00000013526 14755062442 021236 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2011-2015 Codership Oy // #include "galera_gcs.hpp" namespace galera { DummyGcs::DummyGcs(gu::Config& config, gcache::GCache& cache, int repl_proto_ver, int appl_proto_ver, const char* node_name, const char* node_incoming) : gconf_ (&config), gcache_ (&cache), mtx_ (), cond_ (), global_seqno_ (0), local_seqno_ (0), uuid_ (NULL, 0), last_applied_ (GCS_SEQNO_ILL), state_ (S_OPEN), schedule_ (0), cc_ (0), cc_size_ (0), my_name_ (node_name ? node_name : "not specified"), incoming_ (node_incoming ? node_incoming : "not given"), repl_proto_ver_(repl_proto_ver), appl_proto_ver_(appl_proto_ver), report_last_applied_(false) {} DummyGcs::DummyGcs() : gconf_ (0), gcache_ (0), mtx_ (), cond_ (), global_seqno_ (0), local_seqno_ (0), uuid_ (NULL, 0), last_applied_ (GCS_SEQNO_ILL), state_ (S_OPEN), schedule_ (0), cc_ (0), cc_size_ (0), my_name_ ("not specified"), incoming_ ("not given"), repl_proto_ver_(1), appl_proto_ver_(1), report_last_applied_(false) {} DummyGcs::~DummyGcs() { gu::Lock lock(mtx_); assert(0 == schedule_); if (cc_) { assert (cc_size_ > 0); ::free(cc_); } } ssize_t DummyGcs::generate_cc (bool primary) { gcs_act_cchange cc; gcs_node_state_t const my_state (primary ? GCS_NODE_STATE_JOINED : GCS_NODE_STATE_NON_PRIM); if (primary) { ++global_seqno_; cc.seqno = global_seqno_; cc.conf_id = 1; cc.uuid = *uuid_.ptr(); cc.repl_proto_ver = repl_proto_ver_; cc.appl_proto_ver = appl_proto_ver_; /* we have single member here */ gcs_act_cchange::member m; m.uuid_ = *uuid_.ptr(); m.name_ = my_name_; m.incoming_ = incoming_; m.state_ = my_state; cc.memb.push_back(m); } else { cc.seqno = GCS_SEQNO_ILL; cc.conf_id = -1; } cc_size_ = cc.write(&cc_); if (!cc_) { cc_size_ = 0; return -ENOMEM; } return cc_size_; } ssize_t DummyGcs::connect(const std::string& cluster_name, const std::string& cluster_url, bool bootstrap) { gu::Lock lock(mtx_); ssize_t ret = generate_cc (true); if (ret > 0) { cond_.signal(); ret = 0; } return ret; } ssize_t DummyGcs::set_initial_position(const gu::GTID& gtid) { gu::Lock lock(mtx_); if (gtid.uuid() != GU_UUID_NIL && gtid.seqno() >= 0) { uuid_ = gtid.uuid(); global_seqno_ = gtid.seqno(); } return 0; } void DummyGcs::close() { log_info << "Closing DummyGcs"; gu::Lock lock(mtx_); generate_cc (false); // state_ = S_CLOSED; cond_.broadcast(); // usleep(100000); // 0.1s } ssize_t DummyGcs::generate_seqno_action (gcs_action& act, gcs_act_type_t type) { gcs_seqno_t* const seqno (static_cast(::malloc(sizeof(gcs_seqno_t)))); if (!seqno) return -ENOMEM; *seqno = global_seqno_; ++local_seqno_; act.buf = seqno; act.size = sizeof(*seqno); act.seqno_l = local_seqno_; act.type = type; return act.size; } ssize_t DummyGcs::recv(gcs_action& act) { act.seqno_g = GCS_SEQNO_ILL; act.seqno_l = GCS_SEQNO_ILL; gu::Lock lock(mtx_); do { if (cc_) { ++local_seqno_; act.buf = cc_; act.size = cc_size_; act.seqno_l = local_seqno_; act.type = GCS_ACT_CCHANGE; cc_ = 0; cc_size_ = 0; gcs_act_cchange const cc(act.buf, act.size); act.seqno_g = (cc.conf_id >= 0 ? 0 : -1); int const my_idx(act.seqno_g); if (my_idx < 0) { assert (0 == cc.memb.size()); state_ = S_CLOSED; } else { assert (1 == cc.memb.size()); state_ = S_CONNECTED; } return act.size; } else if (S_CONNECTED == state_) { ssize_t ret = generate_seqno_action(act, GCS_ACT_SYNC); if (ret > 0) state_ = S_SYNCED; return ret; } else if (report_last_applied_) { report_last_applied_ = false; return generate_seqno_action(act, GCS_ACT_COMMIT_CUT); } } while (state_ > S_OPEN && (lock.wait(cond_), true)); switch (state_) { case S_OPEN: return -ENOTCONN; case S_CLOSED: return 0; default: abort(); } } ssize_t DummyGcs::interrupt(ssize_t handle) { log_fatal << "Attempt to interrupt handle: " << handle; abort(); return -ENOSYS; } } galera-4-26.4.22/galera/src/write_set.cpp000644 000162 177776 00000006614 14755062442 021254 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2013 Codership Oy // #include "write_set.hpp" #include "gu_serialize.hpp" #include "gu_logger.hpp" size_t galera::WriteSet::serialize(gu::byte_t* buf, size_t buf_len, size_t offset) const { offset = gu::serialize4(keys_, buf, buf_len, offset); offset = gu::serialize4(data_, buf, buf_len, offset); return offset; } size_t galera::WriteSet::unserialize(const gu::byte_t* buf, size_t buf_len, size_t offset) { keys_.clear(); offset = gu::unserialize4(buf, buf_len, offset, keys_); offset = gu::unserialize4(buf, buf_len, offset, data_); return offset; } size_t galera::WriteSet::serial_size() const { return (gu::serial_size4(keys_) + gu::serial_size4(data_)); } std::pair galera::WriteSet::segment(const gu::byte_t* buf, size_t buf_len, size_t offset) { uint32_t data_len; offset = gu::unserialize4(buf, buf_len, offset, data_len); if (gu_unlikely(offset + data_len > buf_len)) { #ifdef NDEBUG gu_throw_error(EMSGSIZE); #else gu_throw_error(EMSGSIZE) << "offset: " << offset << ", data_len: " << data_len << ", buf_len: " << buf_len; #endif /* NDEBUG */ } return std::pair(offset, data_len); } size_t galera::WriteSet::keys(const gu::byte_t* buf, size_t buf_len, size_t offset, int version, KeySequence& ks) { std::pair seg(segment(buf, buf_len, offset)); offset = seg.first; const size_t seg_end(seg.first + seg.second); assert(seg_end <= buf_len); while (offset < seg_end) { KeyOS key(version); if ((offset = key.unserialize(buf, buf_len, offset)) == 0) { gu_throw_fatal << "failed to unserialize key"; } ks.push_back(key); } assert(offset == seg_end); return offset; } void galera::WriteSet::append_key(const KeyData& kd) { KeyOS key (kd.proto_ver, kd.parts, kd.parts_num, (kd.shared() ? galera::KeyOS::F_SHARED : 0) ); if (kd.shared()) assert(key.flags() & galera::KeyOS::F_SHARED); else assert(!(key.flags() & galera::KeyOS::F_SHARED)); const size_t hash(key.hash()); std::pair range(key_refs_.equal_range(hash)); for (KeyRefMap::const_iterator i(range.first); i != range.second; ++i) { KeyOS cmp(version_); (void)cmp.unserialize(&keys_[0], keys_.size(), i->second); if (key == cmp && key.flags() == cmp.flags()) return; } size_t key_size(key.serial_size()); size_t offset(keys_.size()); keys_.resize(offset + key_size); (void)key.serialize(&keys_[0], keys_.size(), offset); (void)key_refs_.insert(std::make_pair(hash, offset)); } void galera::WriteSet::get_keys(KeySequence& s) const { size_t offset(0); while (offset < keys_.size()) { KeyOS key(version_); if ((offset = key.unserialize(&keys_[0], keys_.size(), offset)) == 0) { gu_throw_fatal << "failed to unserialize key"; } s.push_back(key); } assert(offset == keys_.size()); } galera-4-26.4.22/galera/src/SConscript000644 000162 177776 00000003730 14755062442 020551 0ustar00jenkinsnogroup000000 000000 Import('env') libgaleraxx_env = env.Clone() # Include paths libgaleraxx_env.Append(CPPPATH = Split(''' # #/common #/galerautils/src #/gcache/src #/gcs/src ''')) libgaleraxx_srcs = [ 'mapped_buffer.cpp', 'key_data.cpp', 'write_set.cpp', 'data_set.cpp', 'key_set.cpp', 'write_set_ng.cpp', 'trx_handle.cpp', 'key_entry_os.cpp', 'wsdb.cpp', 'certification.cpp', 'galera_service_thd.cpp', 'wsrep_params.cpp', 'replicator_smm_params.cpp', 'gcs_action_source.cpp', 'galera_info.cpp', 'replicator.cpp', 'ist_proto.cpp', 'ist.cpp', 'gcs_dummy.cpp', 'saved_state.cpp', 'galera_view.cpp' ] objs = libgaleraxx_env.Object(libgaleraxx_srcs) env.Append(LIBGALERA_OBJS = libgaleraxx_env.SharedObject(libgaleraxx_srcs)) # Environment for multimaster library build mmlib_env = libgaleraxx_env.Clone() mmlib_env.Append(CPPFLAGS = ' -DGALERA_MULTIMASTER') mmlib_env.Replace(SHOBJPREFIX = 'libmmgalera++-') mmlib_srcs = [ 'replicator_smm.cpp', 'replicator_str.cpp', 'replicator_smm_stats.cpp' ] mm_objs = mmlib_env.Object(mmlib_srcs) env.Append(LIBMMGALERA_OBJS = mmlib_env.SharedObject(mmlib_srcs)) # Environment to compile provider unit (part of multimaster library) # This is needed to hardcode version and revision mmprovider_env = mmlib_env.Clone() Import ('GALERA_VER', 'GALERA_REV') mmprovider_env.Append(CPPFLAGS = ' -DGALERA_VER=\\"' + GALERA_VER + '\\"') mmprovider_env.Append(CPPFLAGS = ' -DGALERA_REV=\\"' + GALERA_REV + '\\"') mmprovider_srcs = [ 'wsrep_provider.cpp' ] mm_objs += mmprovider_env.Object(mmprovider_srcs) env.Append(LIBMMGALERA_OBJS = mmprovider_env.SharedObject(mmprovider_srcs)) libgaleraxx_env.StaticLibrary('galera++', objs + mm_objs) galera-4-26.4.22/galera/src/replicator.hpp000644 000162 177776 00000014432 14755062442 021415 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2017 Codership Oy // #ifndef GALERA_REPLICATOR_HPP #define GALERA_REPLICATOR_HPP #include "wsrep_api.h" #include "galera_exception.hpp" #include "trx_handle.hpp" struct gcs_action; #include #include namespace galera { class Statement; class RowId; //! @class Galera // // @brief Abstract Galera replicator interface class Replicator { public: struct Param { static std::string const debug_log; #ifdef GU_DBUG_ON static std::string const dbug; static std::string const signal; #endif // GU_DBUG_ON }; static const char* const TRIVIAL_SST; static const char* const NO_SST; typedef enum { S_DESTROYED, S_CLOSED, S_CONNECTED, S_JOINING, S_JOINED, S_SYNCED, S_DONOR } State; Replicator() { } virtual ~Replicator() { } virtual wsrep_status_t connect(const std::string& cluster_name, const std::string& cluster_url, const std::string& state_donor, bool bootstrap) = 0; virtual wsrep_status_t close() = 0; virtual wsrep_status_t async_recv(void* recv_ctx) = 0; virtual wsrep_cap_t capabilities() const = 0; virtual int trx_proto_ver() const = 0; virtual int repl_proto_ver() const = 0; virtual TrxHandleMasterPtr get_local_trx(wsrep_trx_id_t, bool) = 0; virtual void discard_local_trx(TrxHandleMaster* trx_id) = 0; virtual TrxHandleMasterPtr local_conn_trx(wsrep_conn_id_t conn_id, bool create) = 0; virtual void discard_local_conn_trx(wsrep_conn_id_t conn_id) = 0; virtual wsrep_status_t replicate(TrxHandleMaster& trx, wsrep_trx_meta_t* meta, const wsrep_seq_cb_t* seq_cb) = 0; virtual wsrep_status_t certify(TrxHandleMaster& trx, wsrep_trx_meta_t* meta) = 0; virtual wsrep_status_t replay_trx(TrxHandleMaster& trx, TrxHandleLock& lock, void* replay_ctx) = 0; virtual wsrep_status_t abort_trx(TrxHandleMaster& trx, wsrep_seqno_t bf_seqno, wsrep_seqno_t* victim_seqno) = 0; virtual wsrep_status_t sync_wait(wsrep_gtid_t* upto, int tout, wsrep_gtid_t* gtid) = 0; virtual wsrep_status_t last_committed_id(wsrep_gtid_t* gtid) const = 0; virtual wsrep_status_t to_isolation_begin(TrxHandleMaster& trx, wsrep_trx_meta_t* meta) = 0; virtual wsrep_status_t to_isolation_end(TrxHandleMaster& trx, const wsrep_buf_t* err) = 0; virtual wsrep_status_t preordered_collect(wsrep_po_handle_t& handle, const struct wsrep_buf* data, size_t count, bool copy) = 0; virtual wsrep_status_t preordered_commit(wsrep_po_handle_t& handle, const wsrep_uuid_t& source, uint64_t flags, int pa_range, bool commit) =0; virtual wsrep_status_t sst_sent(const wsrep_gtid_t& state_id, int rcode) = 0; virtual wsrep_status_t sst_received(const wsrep_gtid_t& state_id, const wsrep_buf_t* state, int rcode) = 0; // action source interface virtual void process_trx(void* recv_ctx, const TrxHandleSlavePtr& trx) = 0; virtual void process_commit_cut(wsrep_seqno_t seq, wsrep_seqno_t seqno_l) = 0; virtual void process_conf_change(void* recv_ctx, const struct gcs_action& cc) = 0; virtual void process_state_req(void* recv_ctx, const void* req, size_t req_size, wsrep_seqno_t seqno_l, wsrep_seqno_t donor_seq) = 0; virtual void process_join(wsrep_seqno_t seqno, wsrep_seqno_t seqno_l) =0; virtual void process_sync(wsrep_seqno_t seqno_l) = 0; virtual void process_vote(wsrep_seqno_t seq, int64_t code, wsrep_seqno_t seqno_l) = 0; virtual const struct wsrep_stats_var* stats_get() const = 0; virtual void stats_reset() = 0; // static void stats_free(struct wsrep_stats_var*) must be declared in // the child class /*! @throws NotFound */ virtual void param_set (const std::string& key, const std::string& value) = 0; /*! @throws NotFound */ virtual std::string param_get (const std::string& key) const = 0; virtual const gu::Config& params() const = 0; virtual wsrep_seqno_t pause() = 0; virtual void resume() = 0; virtual void desync() = 0; virtual void resync() = 0; virtual const wsrep_uuid_t& source_id() const = 0; virtual void cancel_seqnos(wsrep_seqno_t seqno_l, wsrep_seqno_t seqno_g) = 0; virtual bool corrupt() const = 0; static void register_params(gu::Config&); }; } #endif // GALERA_REPLICATOR_HPP galera-4-26.4.22/galera/src/saved_state.hpp000644 000162 177776 00000003355 14755062442 021555 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2012-2018 Codership Oy // #ifndef GALERA_SAVED_STATE_HPP #define GALERA_SAVED_STATE_HPP #include "gu_atomic.hpp" #include "gu_mutex.hpp" #include "gu_lock.hpp" #include "wsrep_api.h" #include #include namespace galera { class SavedState { public: SavedState (const std::string& file); ~SavedState (); void get (wsrep_uuid_t& u, wsrep_seqno_t& s, bool& safe_to_bootstrap); void set (const wsrep_uuid_t& u, wsrep_seqno_t s, bool safe_to_bootstrap); void mark_unsafe(); void mark_safe(); void mark_corrupt(); void mark_uncorrupt(const wsrep_uuid_t& u, wsrep_seqno_t s); bool corrupt() const { return corrupt_; } void stats(long& marks, long& locks, long& writes) { marks = total_marks_(); locks = total_locks_; writes = total_writes_; } private: FILE* fs_; const std::string filename_; wsrep_uuid_t uuid_; wsrep_seqno_t seqno_; bool safe_to_bootstrap_; gu::Atomic unsafe_; bool corrupt_; /* this mutex is needed because mark_safe() and mark_corrupt() will be * called outside local monitor, so race is possible */ gu::Mutex mtx_; wsrep_uuid_t written_uuid_; ssize_t current_len_; gu::Atomic total_marks_; long total_locks_; long total_writes_; void write_file (const wsrep_uuid_t& u, const wsrep_seqno_t s, bool safe_to_bootstrap); SavedState (const SavedState&); SavedState& operator=(const SavedState&); }; /* class SavedState */ } /* namespace galera */ #endif /* GALERA_SAVED_STATE_HPP */ galera-4-26.4.22/galera/src/write_set_ng.hpp000644 000162 177776 00000072661 14755062442 021752 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2013-2017 Codership Oy // /* * Planned writeset composition (not to scale): * * [WS header][ key set ][ data set ][ unordered set ] * * WS header contains common info: total size, set versions etc. * Key set and data set are always present, unordered set is optional. */ #ifndef GALERA_WRITE_SET_NG_HPP #define GALERA_WRITE_SET_NG_HPP #include "wsrep_api.h" #include "key_set.hpp" #include "data_set.hpp" #include "gu_serialize.hpp" #include "gu_vector.hpp" #include #include #include #include namespace galera { class WriteSetNG { public: static int const MAX_SIZE = 0x7fffffff; static int const MAX_PA_RANGE = 0x0000ffff; enum Version { VER3 = 3, VER4, VER5, VER6 }; /* Max header version that we can understand */ static Version const MAX_VERSION = VER6; /* Parses beginning of the header to detect writeset version and * returns it as raw integer for backward compatibility * static Version version(int v) will convert it to enum */ static int version(const void* const buf, size_t const buflen) { if (gu_likely(buflen >= 4)) { const gu::byte_t* const b(static_cast(buf)); if (b[0] == Header::MAGIC_BYTE && b[1] >= ((VER3 << 4) | VER3) && b[2] >= 32 /* header size will hardly ever go below 32 */) { int const min_ver(b[1] & 0x0f); int const max_ver(b[1] >> 4); if (min_ver <= max_ver) /* sanity check */ { /* supported situations: return max supported version */ if (max_ver < MAX_VERSION) return max_ver; if (min_ver <= MAX_VERSION) return MAX_VERSION; /* unsupported situation: minimum required version is * greater than maximum known */ return min_ver; } } else if (0 == b[1] && 0 == b[2] && b[3] <= 2) { /* header from 2.x and before */ return b[3]; } /* unrecognized header, fall through to error */ } return -1; } static Version version(int v) { switch (v) { case VER3: return VER3; case VER4: return VER4; case VER5: return VER5; case VER6: return VER6; } gu_throw_error (EPROTO) << "Unrecognized writeset version: " << v; } /* These flags should be fixed to wire protocol version and so * technically can't be initialized to WSREP_FLAG_xxx macros as the * latter may arbitrarily change. */ enum Flags { F_COMMIT = 1 << 0, F_ROLLBACK = 1 << 1, F_TOI = 1 << 2, F_PA_UNSAFE = 1 << 3, F_COMMUTATIVE = 1 << 4, F_NATIVE = 1 << 5, F_BEGIN = 1 << 6, F_PREPARE = 1 << 7, /* * reserved for provider extension */ F_CERTIFIED = 1 << 14, // needed to correctly interprete pa_range // field (VER5 and up) F_PREORDERED = 1 << 15 // (VER5 and up) }; static bool const FLAGS_MATCH_API_FLAGS = (WSREP_FLAG_TRX_END == F_COMMIT && WSREP_FLAG_ROLLBACK == F_ROLLBACK && WSREP_FLAG_ISOLATION == F_TOI && WSREP_FLAG_PA_UNSAFE == F_PA_UNSAFE && WSREP_FLAG_COMMUTATIVE == F_COMMUTATIVE && WSREP_FLAG_NATIVE == F_NATIVE && WSREP_FLAG_TRX_START == F_BEGIN && WSREP_FLAG_TRX_PREPARE == F_PREPARE); static uint32_t wsrep_flags_to_ws_flags (uint32_t flags); typedef gu::RecordSet::GatherVector GatherVector; /* TODO: separate metadata access from physical representation in * future versions */ class Header { public: static unsigned char const MAGIC_BYTE = 'G'; static Version version(const gu::Buf& buf) { /* the following will throw if version is not supported */ return WriteSetNG::version (WriteSetNG::version(buf.ptr, buf.size)); } static unsigned char size(Version ver) { switch (ver) { case VER3: // fall through case VER4: // fall through case VER5: // fall through case VER6: { GU_COMPILE_ASSERT(0 == (V3_SIZE % GU_MIN_ALIGNMENT), unaligned_header_size); return V3_SIZE; } } log_fatal << "Unknown writeset version: " << ver; abort(); // want to dump core right here } /* This is for WriteSetOut */ explicit Header (Version ver) : local_(), ptr_(local_), ver_(ver), size_(size(ver)), chksm_() { assert((uintptr_t(ptr_) % GU_WORD_BYTES) == 0); assert (size_t(size_) <= sizeof(local_)); } size_t gather (KeySet::Version kver, DataSet::Version const dver, bool unord, bool annot, uint16_t flags, const wsrep_uuid_t& source, const wsrep_conn_id_t& conn, const wsrep_trx_id_t& trx, GatherVector& out); /* records last_seen, timestamp and CRC before replication */ void finalize(wsrep_seqno_t ls, int pa_range); /* records partial seqno, pa_range, timestamp and CRC before * replication (for preordered events)*/ void finalize_preordered(uint16_t pa_range) { finalize(0, pa_range); } /* This is for WriteSetIn */ explicit Header (const gu::Buf& buf) : local_(), ptr_ (static_cast(const_cast(buf.ptr))), ver_ (version(buf)), size_ (check_size(ver_, ptr_, buf.size)), chksm_(ver_, ptr_, size_) { assert((uintptr_t(ptr_) % GU_WORD_BYTES) == 0); } Header () : local_(), ptr_(NULL), ver_(), size_(0), chksm_() {} /* for late WriteSetIn initialization */ void read_buf (const gu::Buf& buf) { ver_ = version(buf); ptr_ = static_cast(const_cast(buf.ptr)); gu_trace(size_ = check_size (ver_, ptr_, buf.size)); Checksum::verify(ver_, ptr_, size_); } Version version() const { return ver_; } unsigned char size() const { return size_; } const gu::byte_t* ptr() const { return ptr_; } KeySet::Version keyset_ver() const { return KeySet::version((ptr_[V3_SETS_OFF] & 0xf0) >> 4); } bool has_keys() const { return keyset_ver() != KeySet::EMPTY; } bool has_unrd() const { return (ptr_[V3_SETS_OFF] & V3_UNORD_FLAG); } bool has_annt() const { return (ptr_[V3_SETS_OFF] & V3_ANNOT_FLAG); } DataSet::Version dataset_ver() const { return DataSet::version((ptr_[V3_SETS_OFF] & 0x0c) >> 2); } DataSet::Version unrdset_ver() const { return has_unrd() ? dataset_ver() : DataSet::EMPTY; } DataSet::Version anntset_ver() const { return has_annt() ? dataset_ver() : DataSet::EMPTY; } uint16_t flags() const { uint16_t ret; gu::unserialize2(ptr_, V3_FLAGS_OFF, ret); return ret; } uint16_t pa_range() const { uint16_t ret; gu::unserialize2(ptr_, V3_PA_RANGE_OFF, ret); return ret; } wsrep_seqno_t last_seen() const { assert (pa_range() == 0 || version() >= VER5); return seqno_priv(); } wsrep_seqno_t seqno() const { return seqno_priv(); } long long timestamp() const { long long ret; gu::unserialize8(ptr_, V3_TIMESTAMP_OFF, ret); return ret; } const wsrep_uuid_t& source_id() const { /* This one is tricky. I would not like to create a copy * of 16 bytes for the sole purpose of referencing it when * alignment in the buffer is already guaranteed */ assert(uintptr_t(ptr_ + V3_SOURCE_ID_OFF)%GU_WORD_BYTES == 0); return *(reinterpret_cast (ptr_ + V3_SOURCE_ID_OFF)); } wsrep_conn_id_t conn_id() const { wsrep_conn_id_t ret; gu::unserialize8(ptr_, V3_CONN_ID_OFF, ret); return ret; } wsrep_trx_id_t trx_id() const { wsrep_trx_id_t ret; gu::unserialize8(ptr_, V3_TRX_ID_OFF, ret); return ret; } const gu::byte_t* payload() const { return ptr_ + size(); } /* to set seqno and parallel applying range after certification */ void set_seqno(wsrep_seqno_t seqno, uint16_t pa_range); gu::Buf copy(bool include_keys, bool include_unrd) const; private: static ssize_t check_size (Version const ver, const gu::byte_t* const buf, ssize_t const bufsize) { assert (bufsize > 4); ssize_t const hsize(buf[V3_HEADER_SIZE_OFF]); if (gu_unlikely(hsize > bufsize)) { gu_throw_error (EMSGSIZE) << "Input buffer size " << bufsize << " smaller than header size " << hsize; } return hsize; } static int const V3_CHECKSUM_SIZE = 8; class Checksum { public: typedef uint64_t type_t; static void compute (const void* ptr, size_t size, type_t& value) { gu::FastHash::digest (ptr, size, value); } static void verify (Version ver, const void* ptr, ssize_t size); Checksum () {} Checksum (Version ver, const void* ptr, ssize_t size) { verify (ver, ptr, size); } private: GU_COMPILE_ASSERT(sizeof(type_t) == V3_CHECKSUM_SIZE, uhoh); }; static unsigned char const V3_ANNOT_FLAG = 0x01; static unsigned char const V3_UNORD_FLAG = 0x02; /* Fist 8 bytes of header: 0: 'G' - "magic" byte 1: bits 4-7: header version bits 0-3: minimum compatible version 2: header size (payload offset) 3: bits 4-7: keyset version bits 2-3: dataset version bit 1: has unordered set bit 0: has annotation 4-5: flags 6-7: PA range all multibyte integers are in little-endian encoding */ static int const V3_MAGIC_OFF = 0; static int const V3_HEADER_VERS_OFF = V3_MAGIC_OFF + 1; static int const V3_HEADER_SIZE_OFF = V3_HEADER_VERS_OFF + 1; static int const V3_SETS_OFF = V3_HEADER_SIZE_OFF + 1; static int const V3_FLAGS_OFF = V3_SETS_OFF + 1; static int const V3_PA_RANGE_OFF = V3_FLAGS_OFF + 2; static int const V3_LAST_SEEN_OFF = V3_PA_RANGE_OFF + 2; static int const V3_SEQNO_OFF = V3_LAST_SEEN_OFF; // seqno takes place of last seen static int const V3_TIMESTAMP_OFF = V3_LAST_SEEN_OFF + 8; static int const V3_SOURCE_ID_OFF = V3_TIMESTAMP_OFF + 8; static int const V3_CONN_ID_OFF = V3_SOURCE_ID_OFF + 16; static int const V3_TRX_ID_OFF = V3_CONN_ID_OFF + 8; static int const V3_CRC_OFF = V3_TRX_ID_OFF + 8; static int const V3_SIZE = V3_CRC_OFF + 8; // 64 struct Offsets { int const header_ver_; int const header_size_; int const sets_; int const flags_; int const pa_range_; int const last_seen_; int const seqno_; int const timestamp_; int const source_id_; int const conn_id_; int const trx_id_; int const crc_; Offsets(int, int, int, int, int, int, int, int, int, int, int, int); }; static Offsets const V3; static int const MAX_HEADER_SIZE = V3_SIZE; mutable gu::byte_t local_[MAX_HEADER_SIZE]; gu::byte_t* ptr_; Version ver_; gu::byte_t size_; Checksum chksm_; wsrep_seqno_t seqno_priv() const { wsrep_seqno_t ret; gu::unserialize8(ptr_, V3_LAST_SEEN_OFF, ret); return ret; } static void update_checksum(gu::byte_t* const ptr, size_t const size) { Checksum::type_t cval; Checksum::compute (ptr, size, cval); gu::serialize(cval, ptr, size); } }; /* class Header */ private: /* this assert should be removed when wsrep API flags become * explicitly incompatible with wirteset flags */ GU_COMPILE_ASSERT(FLAGS_MATCH_API_FLAGS, flags_incompatible); template static inline uint32_t wsrep_flags_to_ws_flags_tmpl (uint32_t const flags) { assert(0); // remove when needed uint32_t ret(0); if (flags & WSREP_FLAG_TRX_END) ret |= F_COMMIT; if (flags & WSREP_FLAG_ROLLBACK) ret |= F_ROLLBACK; if (flags & WSREP_FLAG_ISOLATION) ret |= F_TOI; if (flags & WSREP_FLAG_PA_UNSAFE) ret |= F_PA_UNSAFE; if (flags & WSREP_FLAG_COMMUTATIVE) ret |= F_COMMUTATIVE; if (flags & WSREP_FLAG_NATIVE) ret |= F_NATIVE; if (flags & WSREP_FLAG_TRX_START) ret |= F_BEGIN; if (flags & WSREP_FLAG_TRX_PREPARE) ret |= F_PREPARE; return ret; } }; /* class WriteSetNG */ template <> inline uint32_t WriteSetNG::wsrep_flags_to_ws_flags_tmpl(uint32_t const flags) { return flags; } inline uint32_t WriteSetNG::wsrep_flags_to_ws_flags (uint32_t const flags) { return wsrep_flags_to_ws_flags_tmpl(flags); } class WriteSetOut { public: typedef gu::RecordSetOutBase::BaseName BaseName; WriteSetOut (const std::string& dir_name, wsrep_trx_id_t id, KeySet::Version kver, gu::byte_t* reserved, size_t reserved_size, uint16_t flags = 0, gu::RecordSet::Version rsv = gu::RecordSet::VER2, WriteSetNG::Version ver = WriteSetNG::MAX_VERSION, DataSet::Version dver = DataSet::MAX_VERSION, DataSet::Version uver = DataSet::MAX_VERSION, size_t max_size = WriteSetNG::MAX_SIZE) : header_(ver), base_name_(dir_name, id), /* 1/8 of reserved (aligned by 8) goes to key set */ kbn_ (base_name_), keys_ (reserved, (reserved_size >>= 6, reserved_size <<= 3, reserved_size), kbn_, kver, rsv, ver), /* 5/8 of reserved goes to data set */ dbn_ (base_name_), data_ (reserved + reserved_size, reserved_size*5, dbn_, dver, rsv), /* 2/8 of reserved goes to unordered set */ ubn_ (base_name_), unrd_ (reserved + reserved_size*6, reserved_size*2, ubn_, uver,rsv), /* annotation set is not allocated unless requested */ abn_ (base_name_), annt_ (NULL), left_ (max_size - keys_.size() - data_.size() - unrd_.size() - header_.size()), flags_ (flags) { assert ((uintptr_t(reserved) % GU_WORD_BYTES) == 0); } ~WriteSetOut() { delete annt_; } void append_key(const KeyData& k) { left_ -= keys_.append(k); } void append_data(const void* data, size_t data_len, bool store) { left_ -= data_.append(data, data_len, store); } void append_unordered(const void* data, size_t data_len, bool store) { left_ -= unrd_.append(data, data_len, store); } void append_annotation(const void* data, size_t data_len, bool store) { if (NULL == annt_) { annt_ = new DataSetOut(NULL, 0, abn_, DataSet::MAX_VERSION, // use the same version as the dataset data_.gu::RecordSet::version()); left_ -= annt_->size(); } left_ -= annt_->append(data, data_len, store); } void set_flags(uint16_t flags) { flags_ = flags; } void add_flags(uint16_t flags) { flags_ |= flags; } void mark_toi() { flags_ |= WriteSetNG::F_TOI; } void mark_pa_unsafe() { flags_ |= WriteSetNG::F_PA_UNSAFE; } bool is_empty() const { return ((data_.count() + keys_.count() + unrd_.count() + (annt_ ? annt_->count() : 0)) == 0); } /* !!! This returns header without checksum! * * Use finalize() to finalize it. */ size_t gather(const wsrep_uuid_t& source, const wsrep_conn_id_t& conn, const wsrep_trx_id_t& trx, WriteSetNG::GatherVector& out) { gu_trace(check_size()); out->reserve (out->size() + keys_.page_count() + data_.page_count() + unrd_.page_count() + 1 /* global header */); size_t out_size (header_.gather (keys_.version(), data_.version(), unrd_.version() != DataSet::EMPTY, NULL != annt_, flags_, source, conn, trx, out)); out_size += keys_.gather(out); out_size += data_.gather(out); out_size += unrd_.gather(out); if (NULL != annt_) out_size += annt_->gather(out); return out_size; } void finalize(wsrep_seqno_t const ls, int const pa_range) { header_.finalize(ls, pa_range); } /* Serializes wiriteset into a single buffer (for unit test purposes) * set last_seen to -1 if ws was explicitly finalized */ void serialize(std::vector& ret, const wsrep_uuid_t& source, const wsrep_conn_id_t& conn, const wsrep_trx_id_t& trx, const wsrep_seqno_t last_seen, const int pa_range = -1) { WriteSetNG::GatherVector out; size_t const out_size(gather(source, conn, trx, out)); finalize(last_seen, pa_range); ret.clear(); ret.reserve(out_size); /* concatenate all out buffers into ret */ for (size_t i(0); i < out->size(); ++i) { const gu::byte_t* ptr (static_cast(out[i].ptr)); ret.insert (ret.end(), ptr, ptr + out[i].size); } } void finalize_preordered (ssize_t pa_range) { assert (pa_range >= 0); /* By current convention pa_range is off by 1 from wsrep API def. * 0 meaning failed certification. */ pa_range++; header_.finalize_preordered(pa_range); } private: struct BaseNameCommon { const std::string& dir_name_; unsigned long long const id_; BaseNameCommon(const std::string& dir_name, unsigned long long id) : dir_name_(dir_name), id_ (id) {} }; template class BaseNameImpl : public BaseName { const BaseNameCommon& data_; public: BaseNameImpl (const BaseNameCommon& data) : data_(data) {} void print(std::ostream& os) const { os << data_.dir_name_ << "/0x" << std::hex << std::setfill('0') << std::setw(8) << data_.id_ << suffix_; } }; /* class BaseNameImpl */ static const char keys_suffix[]; static const char data_suffix[]; static const char unrd_suffix[]; static const char annt_suffix[]; WriteSetNG::Header header_; BaseNameCommon base_name_; BaseNameImpl kbn_; KeySetOut keys_; BaseNameImpl dbn_; DataSetOut data_; BaseNameImpl ubn_; DataSetOut unrd_; BaseNameImpl abn_; DataSetOut* annt_; ssize_t left_; uint16_t flags_; void check_size() { if (gu_unlikely(left_ < 0)) gu_throw_error (EMSGSIZE) << "Maximum writeset size exceeded by " << -left_; } WriteSetOut (const WriteSetOut&); WriteSetOut& operator= (const WriteSetOut); }; /* class WriteSetOut */ class WriteSetIn { public: WriteSetIn (const gu::Buf& buf, ssize_t const st = SIZE_THRESHOLD) : header_(buf), size_ (buf.size), keys_ (), data_ (), unrd_ (), annt_ (NULL), check_thr_id_(), check_thr_(false), check_ (false) { gu_trace(init(st)); } WriteSetIn () : header_(), size_ (0), keys_ (), data_ (), unrd_ (), annt_ (NULL), check_thr_id_(), check_thr_(false), check_ (false) {} void read_header (const gu::Buf& buf) { assert (0 == size_); assert (false == check_); header_.read_buf (buf); size_ = buf.size; } /* * WriteSetIn(buf) == WriteSetIn() + read_buf(buf) * * @param st threshold at which launch dedicated thread for checksumming * 0 - no checksumming */ void read_buf (const gu::Buf& buf, ssize_t const st = SIZE_THRESHOLD) { read_header (buf); gu_trace(init(st)); } void read_buf (const void* const ptr, ssize_t const len, ssize_t const st = SIZE_THRESHOLD) { assert (ptr != NULL); assert (len >= 0); gu::Buf tmp = { static_cast(ptr), len }; read_buf (tmp, st); } ~WriteSetIn () { if (gu_unlikely(check_thr_)) { /* checksum was performed in a parallel thread */ gu_thread_join (check_thr_id_, NULL); } delete annt_; } WriteSetNG::Version version() const { return header_.version(); } ssize_t size() const { return size_; } uint16_t flags() const { return header_.flags(); } bool is_toi() const { return flags() & WriteSetNG::F_TOI; } bool pa_unsafe() const { return flags() & WriteSetNG::F_PA_UNSAFE; } int pa_range() const { return header_.pa_range(); } bool certified() const { if (gu_likely(version() >= WriteSetNG::VER5)) return (flags() & WriteSetNG::F_CERTIFIED); else return (pa_range()); // VER3 } wsrep_seqno_t last_seen() const { return header_.last_seen(); } wsrep_seqno_t seqno() const { return header_.seqno(); } long long timestamp() const { return header_.timestamp(); } const wsrep_uuid_t& source_id() const { return header_.source_id(); } wsrep_conn_id_t conn_id() const { return header_.conn_id(); } wsrep_trx_id_t trx_id() const { return header_.trx_id(); } const KeySetIn& keyset() const { return keys_; } const DataSetIn& dataset() const { return data_; } const DataSetIn& unrdset() const { return unrd_; } bool annotated() const { return (annt_ != NULL); } void write_annotation(std::ostream& os) const; /* This should be called right after certification verdict is obtained * and before it is finalized. */ void verify_checksum() const /* throws */ { if (gu_unlikely(check_thr_)) { /* checksum was performed in a parallel thread */ gu_thread_join (check_thr_id_, NULL); check_thr_ = false; gu_trace(checksum_fin()); } } uint64_t get_checksum() const { /* since data segment is the only thing that definitely stays * unchanged through WS lifetime, it is the WS signature */ return (data_.get_checksum()); } void set_seqno(wsrep_seqno_t const seqno, int pa_range) { assert (seqno > 0); assert (pa_range >= 0); /* cap PA range by maximum we can represent */ if (gu_unlikely(pa_range > WriteSetNG::MAX_PA_RANGE)) pa_range = WriteSetNG::MAX_PA_RANGE; header_.set_seqno (seqno, pa_range); } typedef gu::Vector GatherVector; /* can return pointer to internal storage: out can be used only * within object scope. */ size_t gather(GatherVector& out, bool include_keys, bool include_unrd) const; private: WriteSetNG::Header header_; ssize_t size_; KeySetIn keys_; DataSetIn data_; DataSetIn unrd_; DataSetIn* annt_; gu_thread_t check_thr_id_; bool mutable check_thr_; bool check_; static size_t const SIZE_THRESHOLD = 1 << 22; /* 4Mb */ void checksum (); /* checksums writeset, stores result in check_ */ void checksum_fin() const { if (gu_unlikely(!check_)) { gu_throw_error(EINVAL) << "Writeset checksum failed"; } } static void* checksum_thread (void* arg) { WriteSetIn* ws(reinterpret_cast(arg)); ws->checksum(); return NULL; } /* late initialization after default constructor */ void init (ssize_t size_threshold); WriteSetIn (const WriteSetIn&); WriteSetIn& operator=(WriteSetIn); }; } /* namespace galera */ #endif // GALERA_WRITE_SET_HPP galera-4-26.4.22/galera/src/key_entry_os.cpp000644 000162 177776 00000003160 14755062442 021752 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2012 Codership Oy // #include "key_entry_os.hpp" #include "trx_handle.hpp" namespace galera { #ifndef NDEBUG void KeyEntryOS::assert_ref(TrxHandleSlave* trx, bool full_key) const { assert(ref_trx_ == 0 || ref_trx_->global_seqno() <= trx->global_seqno()); if (full_key) { assert(ref_full_trx_ == 0 || (ref_full_trx_->global_seqno() <= trx->global_seqno() && ref_trx_ != 0)); } } void KeyEntryOS::assert_unref(TrxHandleSlave* trx) const { if (ref_full_trx_ != 0 && ref_trx_ == 0) { log_fatal << "dereferencing EXCLUSIVE partial key: " << key_ << " by " << trx->global_seqno() << ", while full key referenced by " << ref_full_trx_->global_seqno(); assert(0); } } void KeyEntryOS::assert_ref_shared(TrxHandleSlave* trx, bool full_key) const { assert(ref_shared_trx_ == 0 || ref_shared_trx_->global_seqno() <= trx->global_seqno()); if (full_key) { assert(ref_full_shared_trx_ == 0 || (ref_full_shared_trx_->global_seqno() <= trx->global_seqno() && ref_shared_trx_ != 0)); } } void KeyEntryOS::assert_unref_shared(TrxHandleSlave* trx) const { if (ref_full_shared_trx_ != 0 && ref_shared_trx_ == 0) { log_fatal << "dereferencing SHARED partial key: " << key_ << " by " << trx->global_seqno() << ", while full key referenced by " << ref_full_shared_trx_->global_seqno(); assert(0); } } #endif /* NDEBUG */ } galera-4-26.4.22/galera/src/key_set.hpp000644 000162 177776 00000054665 14755062442 020730 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2013-2018 Codership Oy // #ifndef GALERA_KEY_SET_HPP #define GALERA_KEY_SET_HPP #include "gu_rset.hpp" #include "gu_unordered.hpp" #include "gu_logger.hpp" #include "gu_hexdump.hpp" #include "key_data.hpp" namespace galera { /* forward declarations for KeySet::KeyPart */ class KeySetOut; class KeySet { public: enum Version { EMPTY = 0, FLAT8, /* 8-byte hash (flat) */ FLAT8A, /* 8-byte hash (flat), annotated */ FLAT16, /* 16-byte hash (flat) */ FLAT16A, /* 16-byte hash (flat), annotated */ // TREE8, /* 8-byte hash + full serialized key */ MAX_VERSION = FLAT16A }; static Version version (unsigned int ver) { if (gu_likely (ver <= MAX_VERSION)) return static_cast(ver); throw_version(ver); } static Version version (const std::string& ver); static const char* type(wsrep_key_type_t const t); class Key { public: enum Prefix // this stays for backward compatibility { P_SHARED = 0, P_EXCLUSIVE }; static int const TYPE_MAX = WSREP_KEY_EXCLUSIVE; }; /* class Key */ /* This class describes what commonly would be referred to as a "key". * It is called KeyPart because it does not fully represent a multi-part * key, but only nth part out of N total. * To fully represent a 3-part key p1:p2:p3 one would need 3 such objects: * for parts p1, p1:p2, p1:p2:p3 */ class KeyPart { public: static size_t const TMP_STORE_SIZE = 4096; static size_t const MAX_HASH_SIZE = 16; union TmpStore { gu::byte_t buf[TMP_STORE_SIZE]; gu_word_t align; }; union HashData { gu::byte_t buf[MAX_HASH_SIZE]; gu_word_t align; }; /* This ctor creates a serialized representation of a key in tmp store * from a key hash and optional annotation. */ KeyPart (TmpStore& tmp, const HashData& hash, const wsrep_buf_t* parts, /* for annotation */ Version const ver, int const prefix, int const part_num, int const alignment ) : data_(tmp.buf) { assert(ver > EMPTY && ver <= MAX_VERSION); /* 16 if ver in { FLAT16, FLAT16A }, 8 otherwise */ int const key_size (8 << (static_cast(ver - FLAT16) <= 1)); assert((key_size % alignment) == 0); assert((uintptr_t(tmp.buf) % GU_WORD_BYTES) == 0); assert((uintptr_t(hash.buf) % GU_WORD_BYTES) == 0); ::memcpy (tmp.buf, hash.buf, key_size); /* use lower bits for header: */ /* clear header bits */ gu::byte_t b = tmp.buf[0] & (~HEADER_MASK); /* set prefix */ assert(prefix <= PREFIX_MASK); b |= (prefix & PREFIX_MASK); /* set version */ b |= (ver & VERSION_MASK) << PREFIX_BITS; tmp.buf[0] = b; if (annotated(ver)) { store_annotation(parts, part_num, tmp.buf + key_size, sizeof(tmp.buf) - key_size, alignment); } } /* This ctor uses pointer to a permanently stored serialized key part */ KeyPart (const gu::byte_t* const buf, size_t const size) : data_(buf) { if (gu_likely(size >= 8 && serial_size() <= size)) return; throw_buffer_too_short (serial_size(), size); } explicit KeyPart (const gu::byte_t* ptr = NULL) : data_(ptr) {} /* converts wsrep key type to KeyPart "prefix" depending on writeset * version */ static int prefix(wsrep_key_type_t const ws_type, int const ws_ver) { if (ws_ver >= 0 && ws_ver <= WS_VER_MAX) { switch (ws_type) { case WSREP_KEY_SHARED: return 0; case WSREP_KEY_REFERENCE: return ws_ver < 4 ? KeySet::Key::P_EXCLUSIVE : 1; case WSREP_KEY_UPDATE: return ws_ver < 4 ? KeySet::Key::P_EXCLUSIVE : (ws_ver < 5 ? 1 : 2); case WSREP_KEY_EXCLUSIVE: return ws_ver < 4 ? KeySet::Key::P_EXCLUSIVE : (ws_ver < 5 ? 2 : 3); } } assert(0); throw_bad_type_version(ws_type, ws_ver); } /* The return value is subject to interpretation based on the * writeset version which is done in wsrep_type(int) method */ int prefix() const { return (data_[0] & PREFIX_MASK); } wsrep_key_type_t wsrep_type(int const ws_ver) const { assert(ws_ver >= 0 && ws_ver <= WS_VER_MAX); wsrep_key_type_t ret; switch (prefix()) { case 0: ret = WSREP_KEY_SHARED; break; case 1: ret = ws_ver < 4 ? WSREP_KEY_EXCLUSIVE : WSREP_KEY_REFERENCE; break; case 2: assert(ws_ver >= 4); ret = ws_ver < 5 ? WSREP_KEY_EXCLUSIVE : WSREP_KEY_UPDATE; break; case 3: assert(ws_ver >= 5); ret = WSREP_KEY_EXCLUSIVE; break; default: throw_bad_prefix(prefix()); } assert(prefix() == prefix(ret, ws_ver)); return ret; } static Version version(const gu::byte_t* const buf) { return Version( buf ? (buf[0] >> PREFIX_BITS) & VERSION_MASK : EMPTY); } Version version() const { return KeyPart::version(data_); } KeyPart (const KeyPart& k) : data_(k.data_) {} KeyPart& operator= (const KeyPart& k) { data_ = k.data_; return *this; } /* for hash table */ bool matches (const KeyPart& kp) const { assert (NULL != this->data_); assert (NULL != kp.data_); bool ret(true); // collision by default #if GU_WORDSIZE == 64 const uint64_t* lhs(reinterpret_cast(data_)); const uint64_t* rhs(reinterpret_cast(kp.data_)); #else const uint32_t* lhs(reinterpret_cast(data_)); const uint32_t* rhs(reinterpret_cast(kp.data_)); #endif /* WORDSIZE */ switch (std::min(version(), kp.version())) { case EMPTY: assert(0); throw_match_empty_key(version(), kp.version()); case FLAT16: case FLAT16A: #if GU_WORDSIZE == 64 ret = (lhs[1] == rhs[1]); #else ret = (lhs[2] == rhs[2] && lhs[3] == rhs[3]); #endif /* WORDSIZE */ /* fall through */ case FLAT8: case FLAT8A: /* shift is to clear up the header */ #if GU_WORDSIZE == 64 ret = ret && ((gtoh64(lhs[0]) >> HEADER_BITS) == (gtoh64(rhs[0]) >> HEADER_BITS)); #else ret = ret && (lhs[1] == rhs[1] && (gtoh32(lhs[0]) >> HEADER_BITS) == (gtoh32(rhs[0]) >> HEADER_BITS)); #endif /* WORDSIZE */ } return ret; } size_t hash () const { /* Now this leaves uppermost bits always 0. * How bad is it in practice? Is it reasonable to assume that only * lower bits are used in unordered set? */ size_t ret(gu::gtoh(reinterpret_cast(data_)[0]) >> HEADER_BITS); return ret; // (ret ^ (ret << HEADER_BITS)) to cover 0 bits } static size_t serial_size (const gu::byte_t* const buf, size_t const size) { Version const ver(version(buf)); return serial_size (ver, buf, size); } size_t serial_size () const { return KeyPart::serial_size(data_, -1U); } void print (std::ostream& os) const; void swap (KeyPart& other) { using std::swap; swap(data_, other.data_); } const gu::byte_t* ptr() const { return data_; } protected: friend class KeySetOut; /* update data pointer */ void update_ptr(const gu::byte_t* ptr) const { data_ = ptr; } /* update storage of KeyPart already inserted in unordered set */ void store(gu::RecordSetOut& rs) const { data_ = rs.append(data_, serial_size(), true, true).first; // log_info << "Stored key of size: " << serial_size(); } private: static unsigned int const PREFIX_BITS = 2; static gu::byte_t const PREFIX_MASK = (1 << PREFIX_BITS) - 1; static unsigned int const VERSION_BITS = 3; static gu::byte_t const VERSION_MASK = (1 << VERSION_BITS) - 1; static unsigned int const HEADER_BITS = PREFIX_BITS + VERSION_BITS; static gu::byte_t const HEADER_MASK = (1 << HEADER_BITS) - 1; mutable /* to be able to store const object */ const gu::byte_t* data_; // it never owns the buffer static size_t base_size (Version const ver, const gu::byte_t* const buf, size_t const size) { switch (ver) { case FLAT16: case FLAT16A: return 16; case FLAT8: case FLAT8A: return 8; case EMPTY: assert(0); } abort(); } static bool annotated (Version const ver) { return (ver == FLAT16A || ver == FLAT8A); } typedef uint16_t ann_size_t; static size_t serial_size (Version const ver, const gu::byte_t* const buf, size_t const size = -1U) { size_t ret(base_size(ver, buf, size)); assert (ret <= size); if (annotated(ver)) { assert (ret + 2 <= size); ret +=gu::gtoh(*reinterpret_cast(buf + ret)); assert (ret <= size); } return ret; } static size_t store_annotation (const wsrep_buf_t* parts, int part_num, gu::byte_t* buf, int size, int alignment); static void print_annotation (std::ostream& os, const gu::byte_t* buf); static void throw_buffer_too_short (size_t expected, size_t got) GU_NORETURN; static void throw_bad_type_version (wsrep_key_type_t t, int v) GU_NORETURN; static void throw_bad_prefix (gu::byte_t p) GU_NORETURN; static void throw_match_empty_key (Version my, Version other) GU_NORETURN; }; /* class KeyPart */ class KeyPartHash { public: size_t operator() (const KeyPart& k) const { return k.hash(); } }; class KeyPartEqual { public: bool operator() (const KeyPart& l, const KeyPart& r) const { return (l.matches(r)); } }; /* functor KeyPartEqual */ static void throw_version(int) GU_NORETURN; static int const WS_VER_MAX = 6; }; /* class KeySet */ inline void swap (KeySet::KeyPart& a, KeySet::KeyPart& b) { a.swap(b); } inline std::ostream& operator << (std::ostream& os, const KeySet::KeyPart& kp) { kp.print (os); return os; } #if defined(__GNUG__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4) # pragma GCC diagnostic push # endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4) # pragma GCC diagnostic ignored "-Weffc++" #endif class KeySetOut : public gu::RecordSetOut { public: typedef gu::UnorderedSet < KeySet::KeyPart, KeySet::KeyPartHash, KeySet::KeyPartEqual > /* This #if decides whether we use straight gu::UnorderedSet for appended * key parts (0), or go for an optimized version (1). Don't remove it. */ #if 0 KeyParts; #else KeyPartSet; /* This is a naive mock up of an "unordered set" that first tries to use * preallocated set of buckets and falls back to a "real" heap-based * unordered set from STL/TR1 when preallocated one is exhausted. * The goal is to make sure that at least 3 keys can be inserted without * the need for dynamic allocation. * In practice, with 64 "buckets" and search depth of 3, the average * number of inserted keys before there is a need to go for heap is 25. * 128 buckets will give you 45 and 256 - around 80. */ class KeyParts { public: KeyParts() : first_(), second_(NULL), first_size_(0) { ::memset(first_, 0, sizeof(first_)); } ~KeyParts() { delete second_; } /* This iterator class is declared for compatibility with * unordered_set. We may actually use a more simple interface here. */ class iterator { public: iterator(const KeySet::KeyPart* kp) : kp_(kp) {} /* This is sort-of a dirty hack to ensure that first_ array * of KeyParts class can be treated like a POD array. * It uses the fact that the only non-static member of * KeySet::KeyPart is gu::byte_t* and so does direct casts between * pointers. I wish someone could make it cleaner. */ iterator(const gu::byte_t** kp) : kp_(reinterpret_cast(kp)) {} const KeySet::KeyPart* operator -> () const { return kp_; } const KeySet::KeyPart& operator * () const { return *kp_; } bool operator == (const iterator& i) const { return (kp_ == i.kp_); } bool operator != (const iterator& i) const { return (kp_ != i.kp_); } private: const KeySet::KeyPart* kp_; }; const iterator end() { return iterator(static_cast(NULL)); } const iterator find(const KeySet::KeyPart& kp) { unsigned int idx(kp.hash()); for (unsigned int i(0); i < FIRST_DEPTH; ++i, ++idx) { idx &= FIRST_MASK; if (0 !=first_[idx] && KeySet::KeyPart(first_[idx]).matches(kp)) { return iterator(&first_[idx]); } } if (second_ && second_->size() > 0) { KeyPartSet::iterator i2(second_->find(kp)); if (i2 != second_->end()) return iterator(&(*i2)); } return end(); } std::pair insert(const KeySet::KeyPart& kp) { unsigned int idx(kp.hash()); for (unsigned int i(0); i < FIRST_DEPTH; ++i, ++idx) { idx &= FIRST_MASK; if (0 == first_[idx]) { first_[idx] = kp.ptr(); ++first_size_; return std::pair(iterator(&first_[idx]), true); } if (KeySet::KeyPart(first_[idx]).matches(kp)) { return std::pair(iterator(&first_[idx]),false); } } if (!second_) { second_ = new KeyPartSet(); // log_info << "Requesting heap at load factor " // << first_size_ << '/' << FIRST_SIZE << " = " // << (double(first_size_)/FIRST_SIZE); } std::pair res = second_->insert(kp); return std::pair(iterator(&(*res.first)), res.second); } iterator erase(iterator it) { unsigned int idx(it->hash()); for (unsigned int i(0); i < FIRST_DEPTH; ++i, ++idx) { idx &= FIRST_MASK; if (first_[idx] && KeySet::KeyPart(first_[idx]).matches(*it)) { first_[idx] = 0; --first_size_; return iterator(&first_[(idx + 1) & FIRST_MASK]); } } if (second_ && second_->size() > 0) { KeyPartSet::iterator it2(second_->erase(second_->find(*it))); if (it2 != second_->end()) return iterator(&(*it2)); } return end(); } size_t size() const { return (first_size_ + second_->size()); } private: static unsigned int const FIRST_MASK = 0x3f; // 63 static unsigned int const FIRST_SIZE = FIRST_MASK + 1; static unsigned int const FIRST_DEPTH = 3; const gu::byte_t* first_[FIRST_SIZE]; KeyPartSet* second_; unsigned int first_size_; }; #endif /* 1 */ class KeyPart { public: KeyPart (KeySet::Version const ver = KeySet::FLAT16) : hash_ (), part_ (0), value_(0), size_ (0), ver_ (ver), own_ (false) { assert (ver_); } /* to throw in KeyPart() ctor in case it is a duplicate */ class DUPLICATE {}; KeyPart (KeyParts& added, KeySetOut& store, const KeyPart* parent, const KeyData& kd, int const part_num, int const ws_ver, int const alignment); KeyPart (const KeyPart& k) : hash_ (k.hash_), part_ (k.part_), value_(k.value_), size_ (k.size_), ver_ (k.ver_), own_ (k.own_) { assert (ver_); k.own_ = false; } friend void swap (KeyPart& l, KeyPart& r) { using std::swap; swap (l.hash_, r.hash_ ); swap (l.part_, r.part_ ); swap (l.value_, r.value_); swap (l.size_, r.size_ ); swap (l.ver_, r.ver_ ); swap (l.own_, r.own_ ); } KeyPart& operator= (KeyPart k) { swap(*this, k); return *this; } bool match (const void* const v, size_t const s) const { return (size_ == s && !(::memcmp (value_, v, size_))); } int prefix() const { return (part_ ? part_->prefix() : 0); } void acquire() { gu::byte_t* tmp = new gu::byte_t[size_]; std::copy(value_, value_ + size_, tmp); value_ = tmp; own_ = true; } void release() { if (own_) { // log_debug << "released: " << gu::Hexdump(value_, size_, true); delete[] value_; value_ = 0; } own_ = false; } ~KeyPart() { release(); } void print (std::ostream& os) const; typedef gu::RecordSet::GatherVector GatherVector; private: gu::Hash hash_; const KeySet::KeyPart* part_; mutable const gu::byte_t* value_; unsigned int size_; KeySet::Version ver_; mutable bool own_; }; /* class KeySetOut::KeyPart */ KeySetOut () // empty ctor for slave TrxHandle : gu::RecordSetOut(), added_(), prev_ (), new_ (), version_() {} KeySetOut (gu::byte_t* reserved, size_t reserved_size, const BaseName& base_name, KeySet::Version const version, gu::RecordSet::Version const rsv, int const ws_ver) : gu::RecordSetOut ( reserved, reserved_size, base_name, check_type(version), rsv ), added_(), prev_ (), new_ (), version_(version), ws_ver_(ws_ver) { assert (version_ != KeySet::EMPTY); assert ((uintptr_t(reserved) % GU_WORD_BYTES) == 0); assert (ws_ver <= KeySet::WS_VER_MAX); KeyPart zero(version_); prev_().push_back(zero); } ~KeySetOut () {} size_t append (const KeyData& kd); KeySet::Version version () { return count() ? version_ : KeySet::EMPTY; } private: // depending on version we may pack data differently KeyParts added_; gu::Vector prev_; gu::Vector new_; KeySet::Version version_; int ws_ver_; int find_common_ancestor_with_previous(const KeyData&) const; static gu::RecordSet::CheckType check_type (KeySet::Version ver) { switch (ver) { case KeySet::EMPTY: break; /* Can't create EMPTY KeySetOut */ default: return gu::RecordSet::CHECK_MMH128; } KeySet::throw_version(ver); } }; /* class KeySetOut */ inline std::ostream& operator << (std::ostream& os, const KeySetOut::KeyPart& kp) { kp.print (os); return os; } class KeySetIn : public gu::RecordSetIn { public: KeySetIn (KeySet::Version ver, const gu::byte_t* buf, size_t size) : gu::RecordSetIn(buf, size, false), version_(ver) {} KeySetIn () : gu::RecordSetIn(), version_(KeySet::EMPTY) {} void init (KeySet::Version ver, const gu::byte_t* buf, size_t size) { gu::RecordSetIn::init(buf, size, false); version_ = ver; } KeySet::KeyPart const next () const { return gu::RecordSetIn::next(); } private: KeySet::Version version_; }; /* class KeySetIn */ #if defined(__GNUG__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4) # pragma GCC diagnostic pop # endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4) #endif } /* namespace galera */ #endif // GALERA_KEY_SET_HPP galera-4-26.4.22/galera/src/wsrep_params.hpp000644 000162 177776 00000000501 14755062442 021744 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010 Codership Oy // #ifndef WSREP_PARAMS_HPP #define WSREP_PARAMS_HPP #include "wsrep_api.h" #include "replicator.hpp" void wsrep_set_params (galera::Replicator& repl, const char* params); char* wsrep_get_params(const galera::Replicator& repl); #endif /* WSREP_PARAMS_HPP */ galera-4-26.4.22/galera/src/saved_state.cpp000644 000162 177776 00000017424 14755062442 021552 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2012-2018 Codership Oy // #include "saved_state.hpp" #include #include #include "gu_inttypes.hpp" #include #include #include namespace galera { #define VERSION "2.1" #define MAX_SIZE 256 SavedState::SavedState (const std::string& file) : fs_ (0), filename_ (file), uuid_ (WSREP_UUID_UNDEFINED), seqno_ (WSREP_SEQNO_UNDEFINED), safe_to_bootstrap_(true), unsafe_ (0), corrupt_ (false), mtx_ (), written_uuid_ (uuid_), current_len_ (0), total_marks_ (0), total_locks_ (0), total_writes_ (0) { GU_DBUG_EXECUTE("galera_init_invalidate_state", unlink(file.c_str());); std::ifstream ifs(file.c_str()); if (ifs.fail()) { log_warn << "Could not open state file for reading: '" << file << '\''; } fs_ = fopen(file.c_str(), "a"); if (!fs_) { gu_throw_system_error(errno) << "Could not open state file for writing: '" << file << "'. Check permissions and/or disk space."; } // We take exclusive lock on state file in order to avoid possibility // of two Galera replicators sharing the same state file. struct flock flck; flck.l_start = 0; flck.l_len = 0; flck.l_type = F_WRLCK; flck.l_whence = SEEK_SET; if (::fcntl(fileno(fs_), F_SETLK, &flck)) { log_warn << "Could not get exclusive lock on state file: " << file << ": " << ::strerror(errno); return; } std::string version("0.8"); std::string line; while (getline(ifs, line), ifs.good()) { std::istringstream istr(line); std::string param; istr >> param; if (param[0] == '#') { log_debug << "read comment: " << line; } else if (param == "version:") { istr >> version; // nothing to do with this yet log_debug << "read version: " << version; } else if (param == "uuid:") { try { istr >> uuid_; log_debug << "read saved state uuid: " << uuid_; } catch (gu::Exception& e) { log_error << e.what(); uuid_ = WSREP_UUID_UNDEFINED; } } else if (param == "seqno:") { istr >> seqno_; log_debug << "read saved state seqno: " << seqno_; } else if (param == "safe_to_bootstrap:") { istr >> safe_to_bootstrap_; log_debug << "read safe_to_bootstrap: " << safe_to_bootstrap_; } } log_info << "Found saved state: " << uuid_ << ':' << seqno_ << ", safe_to_bootstrap: " << safe_to_bootstrap_; #if 0 // we'll probably have it legal if (seqno_ < 0 && uuid_ != WSREP_UUID_UNDEFINED) { log_warn << "Negative seqno with valid UUID: " << uuid_ << ':' << seqno_ << ". Discarding UUID."; uuid_ = WSREP_UUID_UNDEFINED; } #endif written_uuid_ = uuid_; current_len_ = ftell (fs_); log_debug << "Initialized current_len_ to " << current_len_; if (current_len_ <= MAX_SIZE) { fs_ = freopen (file.c_str(), "r+", fs_); } else // normalize file contents { fs_ = freopen (file.c_str(), "w+", fs_); // truncate current_len_ = 0; set (uuid_, seqno_, safe_to_bootstrap_); } } SavedState::~SavedState () { if (fs_) { // Closing file descriptor should release the lock, but still... struct flock flck; flck.l_start = 0; flck.l_len = 0; flck.l_type = F_UNLCK; flck.l_whence = SEEK_SET; if (::fcntl(fileno(fs_), F_SETLK, &flck)) { log_warn << "Could not unlock state file: " << ::strerror(errno); } fclose(fs_); } } void SavedState::get (wsrep_uuid_t& u, wsrep_seqno_t& s, bool& safe_to_bootstrap) { gu::Lock lock(mtx_); u = uuid_; s = seqno_; safe_to_bootstrap = safe_to_bootstrap_; } void SavedState::set (const wsrep_uuid_t& u, wsrep_seqno_t s, bool safe_to_bootstrap) { gu::Lock lock(mtx_); ++total_locks_; if (corrupt_) return; uuid_ = u; seqno_ = s; safe_to_bootstrap_ = safe_to_bootstrap; if (0 == unsafe_()) write_file (u, s, safe_to_bootstrap); else log_debug << "Not writing state: unsafe counter is " << unsafe_(); } /* the goal of unsafe_, written_uuid_, current_len_ below is * 1. avoid unnecessary mutex locks * 2. if locked - avoid unnecessary file writes * 3. if writing - avoid metadata operations, write over existing space */ void SavedState::mark_unsafe() { ++total_marks_; if (1 == unsafe_.add_and_fetch (1)) { gu::Lock lock(mtx_); ++total_locks_; assert (unsafe_() > 0); if (written_uuid_ != WSREP_UUID_UNDEFINED) { write_file (WSREP_UUID_UNDEFINED, WSREP_SEQNO_UNDEFINED, safe_to_bootstrap_); } } } void SavedState::mark_safe() { ++total_marks_; long count = unsafe_.sub_and_fetch (1); assert (count >= 0); if (0 == count) { gu::Lock lock(mtx_); ++total_locks_; if (0 == unsafe_() && (written_uuid_ != uuid_ || seqno_ >= 0) && !corrupt_) { /* this will write down proper seqno if set() was called too early * (in unsafe state) */ write_file (uuid_, seqno_, safe_to_bootstrap_); } } } void SavedState::mark_corrupt() { gu::Lock lock(mtx_); ++total_locks_; if (corrupt_) return; uuid_ = WSREP_UUID_UNDEFINED; seqno_ = WSREP_SEQNO_UNDEFINED; corrupt_ = true; write_file (WSREP_UUID_UNDEFINED, WSREP_SEQNO_UNDEFINED, safe_to_bootstrap_); } void SavedState::mark_uncorrupt(const wsrep_uuid_t& u, wsrep_seqno_t s) { gu::Lock lock(mtx_); ++total_locks_; if (!corrupt_) return; uuid_ = u; seqno_ = s; unsafe_ = 0; corrupt_ = false; write_file (u, s, safe_to_bootstrap_); } void SavedState::write_file(const wsrep_uuid_t& u, const wsrep_seqno_t s, bool safe_to_bootstrap) { assert (current_len_ <= MAX_SIZE); if (fs_) { if (s >= 0) { log_debug << "Saving state: " << u << ':' << s; } char buf[MAX_SIZE]; int state_len = snprintf (buf, MAX_SIZE - 1, "# GALERA saved state" "\nversion: " VERSION "\nuuid: " GU_UUID_FORMAT "\nseqno: %" PRId64 "\nsafe_to_bootstrap: %d\n", GU_UUID_ARGS(&u), s, safe_to_bootstrap); int write_size; for (write_size = state_len; write_size < current_len_; ++write_size) buf[write_size] = ' '; // overwrite whatever is there currently rewind(fs_); if (fwrite(buf, write_size, 1, fs_) == 0) { log_warn << "write file(" << filename_ << ") failed(" << strerror(errno) << ")"; return; } if (fflush(fs_) != 0) { log_warn << "fflush file(" << filename_ << ") failed(" << strerror(errno) << ")"; return; } if (fsync(fileno(fs_)) < 0) { log_warn << "fsync file(" << filename_ << ") failed(" << strerror(errno) << ")"; return; } current_len_ = state_len; written_uuid_ = u; ++total_writes_; } else { log_debug << "Can't save state: output stream is not open."; } } } /* namespace galera */ galera-4-26.4.22/galera/src/replicator_smm.hpp000644 000162 177776 00000115335 14755062442 022275 0ustar00jenkinsnogroup000000 000000 // // Copyright (C) 2010-2021 Codership Oy // //! @file replicator_smm.hpp // // @brief Galera Synchronous Multi-Master replicator // #ifndef GALERA_REPLICATOR_SMM_HPP #define GALERA_REPLICATOR_SMM_HPP #include "replicator.hpp" #include "progress_callback.hpp" #include "gu_init.h" #include "GCache.hpp" #include "gcs.hpp" #include "monitor.hpp" #include "wsdb.hpp" #include "certification.hpp" #include "trx_handle.hpp" #include "write_set.hpp" #include "galera_service_thd.hpp" #include "fsm.hpp" #include "action_source.hpp" #include "ist.hpp" #include "gu_atomic.hpp" #include "saved_state.hpp" #include "gu_debug_sync.hpp" #include #include namespace galera { class ReplicatorSMM : public Replicator, public ist::EventHandler { public: typedef enum { SST_NONE, SST_WAIT, SST_JOIN_SENT, SST_REQ_FAILED, SST_FAILED } SstState; static const size_t N_STATES = S_DONOR + 1; ReplicatorSMM(const wsrep_init_args* args); ~ReplicatorSMM(); wsrep_cap_t capabilities() const { return capabilities(proto_max_); } int trx_proto_ver() const { return trx_params_.version_; } int repl_proto_ver() const{ return protocol_version_; } wsrep_status_t connect(const std::string& cluster_name, const std::string& cluster_url, const std::string& state_donor, bool bootstrap); wsrep_status_t close(); wsrep_status_t async_recv(void* recv_ctx); TrxHandleMasterPtr get_local_trx(wsrep_trx_id_t trx_id, bool create = false) { return wsdb_.get_trx(trx_params_, uuid_, trx_id, create); } TrxHandleMasterPtr new_trx(const wsrep_uuid_t& uuid, wsrep_trx_id_t trx_id) { return wsdb_.new_trx(trx_params_, uuid, trx_id); } TrxHandleMasterPtr new_local_trx(wsrep_trx_id_t trx_id) { return new_trx(uuid_, trx_id); } void discard_local_trx(TrxHandleMaster* trx) { wsdb_.discard_trx(trx->trx_id()); } TrxHandleMasterPtr local_conn_trx(wsrep_conn_id_t conn_id, bool create) { return wsdb_.get_conn_query(trx_params_, uuid_, conn_id, create); } void discard_local_conn_trx(wsrep_conn_id_t conn_id) { wsdb_.discard_conn_query(conn_id); } void apply_trx(void* recv_ctx, TrxHandleSlave& trx); wsrep_status_t handle_apply_error(TrxHandleSlave& trx, const wsrep_buf_t& error_buf, const std::string& custom_msg); void process_apply_error(TrxHandleSlave&, const wsrep_buf_t&); wsrep_status_t send(TrxHandleMaster& trx, wsrep_trx_meta_t*); wsrep_status_t replicate(TrxHandleMaster& trx, wsrep_trx_meta_t*, const wsrep_seq_cb_t* seq_cb); wsrep_status_t abort_trx(TrxHandleMaster& trx, wsrep_seqno_t bf_seqno, wsrep_seqno_t* victim_seqno); wsrep_status_t certify(TrxHandleMaster& trx, wsrep_trx_meta_t*); wsrep_status_t commit_order_enter_local(TrxHandleMaster& trx); wsrep_status_t commit_order_enter_remote(TrxHandleSlave& trx); wsrep_status_t commit_order_leave(TrxHandleSlave& trx, const wsrep_buf_t* error); wsrep_status_t release_commit(TrxHandleMaster& trx); wsrep_status_t release_rollback(TrxHandleMaster& trx); wsrep_status_t replay_trx(TrxHandleMaster& trx, TrxHandleLock& lock, void* replay_ctx); wsrep_status_t sync_wait(wsrep_gtid_t* upto, int tout, wsrep_gtid_t* gtid); wsrep_status_t last_committed_id(wsrep_gtid_t* gtid) const; wsrep_status_t to_isolation_begin(TrxHandleMaster& trx, wsrep_trx_meta_t* meta); wsrep_status_t to_isolation_end(TrxHandleMaster& trx, const wsrep_buf_t* err); wsrep_status_t preordered_collect(wsrep_po_handle_t& handle, const struct wsrep_buf* data, size_t count, bool copy); wsrep_status_t preordered_commit(wsrep_po_handle_t& handle, const wsrep_uuid_t& source, uint64_t flags, int pa_range, bool commit); wsrep_status_t sst_sent(const wsrep_gtid_t& state_id, int rcode); wsrep_status_t sst_received(const wsrep_gtid_t& state_id, const wsrep_buf_t* state, int rcode); void process_trx(void* recv_ctx, const TrxHandleSlavePtr& trx); void process_commit_cut(wsrep_seqno_t seq, wsrep_seqno_t seqno_l); void submit_view_info(void* recv_ctx, const wsrep_view_info_t* cc); void process_conf_change(void* recv_ctx, const struct gcs_action& cc); void process_state_req(void* recv_ctx, const void* req, size_t req_size, wsrep_seqno_t seqno_l, wsrep_seqno_t donor_seq); void process_join(wsrep_seqno_t seqno, wsrep_seqno_t seqno_l); void process_sync(wsrep_seqno_t seqno_l); void process_vote(wsrep_seqno_t seq, int64_t code,wsrep_seqno_t seqno_l); const struct wsrep_stats_var* stats_get() const; void stats_reset(); void stats_free(struct wsrep_stats_var*); /*! @throws NotFound */ void set_param (const std::string& key, const std::string& value); /*! @throws NotFound */ void param_set (const std::string& key, const std::string& value); std::string param_get (const std::string& key) const; const gu::Config& params() const { return config_; } wsrep_seqno_t pause(); void resume(); void desync(); void resync(); const wsrep_uuid_t& source_id() const { return uuid_; } // IST Action handler interface void ist_trx(const TrxHandleSlavePtr& ts, bool must_apply, bool preload) override; void ist_cc(const gcs_action&, bool must_apply, bool preload) override; void ist_end(const ist::Result&) override; // Cancel local and enter apply monitors for TrxHandle void cancel_monitors_for_local(const TrxHandleSlave& ts) { log_debug << "canceling monitors on behalf of trx: " << ts; assert(ts.local()); assert(ts.global_seqno() > 0); LocalOrder lo(ts); local_monitor_.self_cancel(lo); } // Cancel all monitors for given seqnos void cancel_seqnos(wsrep_seqno_t seqno_l, wsrep_seqno_t seqno_g); // Drain apply and commit monitors up to seqno void drain_monitors(wsrep_seqno_t seqno); class ISTEvent { public: enum Type { T_NULL, // empty T_TRX, // TrxHandleSlavePtr T_VIEW // configuration change }; ISTEvent() : ts_() , view_() , type_(T_NULL) { } ISTEvent(const TrxHandleSlavePtr& ts) : ts_(ts) , view_() , type_(T_TRX) { } ISTEvent(wsrep_view_info_t* view) : ts_() , view_(view) , type_(T_VIEW) { } ISTEvent(const ISTEvent& other) : ts_(other.ts_) , view_(other.view_) , type_(other.type_) { } ISTEvent& operator=(const ISTEvent& other) { ts_ = other.ts_; view_ = other.view_; type_ = other.type_; return *this; } ~ISTEvent() { } Type type() const { return type_; } TrxHandleSlavePtr ts() const { assert(T_TRX == type_); return ts_; } wsrep_view_info_t* view() const { assert(T_VIEW == type_); return view_; } private: TrxHandleSlavePtr ts_; wsrep_view_info_t* view_; Type type_; }; // Helper class to synchronize between IST receiver thread // applier threads. class ISTEventQueue { public: ISTEventQueue() : mutex_(), cond_(), eof_(false), result_(0, ""), queue_() { } void reset() { eof_ = false; result_ = ist::Result{0, ""}; } void eof(const ist::Result& result) { gu::Lock lock(mutex_); eof_ = true; result_ = result; cond_.broadcast(); } // Push back void push_back(const TrxHandleSlavePtr& ts) { gu::Lock lock(mutex_); queue_.push(ISTEvent(ts)); cond_.signal(); } // Push back void push_back(wsrep_view_info_t* view) { gu::Lock lock(mutex_); queue_.push(ISTEvent(view)); cond_.signal(); } // Pop front // // Throws gu::Exception() in case of error for the first // caller which will detect the error. // Returns null in case of EOF ISTEvent pop_front() { gu::Lock lock(mutex_); while (eof_ == false && queue_.empty() == true) { lock.wait(cond_); } ISTEvent ret; if (queue_.empty() == false) { ret = queue_.front(); queue_.pop(); } else { if (result_.error) { int err(result_.error); // Make just one thread to detect the failure result_.error = 0; gu_throw_error(err) << "IST receiver reported failure: '" << result_.error_str << "' (" << err << ")"; } } return ret; } private: gu::Mutex mutex_; gu::Cond cond_; bool eof_; ist::Result result_; std::queue queue_; }; ISTEventQueue ist_event_queue_; void mark_corrupt_and_close() /* mark state as corrupt and try to leave cleanly */ { st_.mark_corrupt(); gu::Lock lock(closing_mutex_); start_closing(); } void on_inconsistency() { cert_.mark_inconsistent(); mark_corrupt_and_close(); } bool corrupt() const { return st_.corrupt(); } struct InitConfig { InitConfig(gu::Config&, const char* node_addr,const char* base_dir); }; class StateRequest { public: virtual int version () const = 0; virtual const void* req () const = 0; virtual ssize_t len () const = 0; virtual const void* sst_req () const = 0; virtual ssize_t sst_len () const = 0; virtual const void* ist_req () const = 0; virtual ssize_t ist_len () const = 0; virtual ~StateRequest() {} }; private: ReplicatorSMM(const ReplicatorSMM&); void operator=(const ReplicatorSMM&); struct Param { static const std::string base_host; static const std::string base_port; static const std::string base_dir; static const std::string proto_max; static const std::string key_format; static const std::string commit_order; static const std::string causal_read_timeout; static const std::string max_write_set_size; }; typedef std::pair Default; struct Defaults { std::map map_; Defaults (); }; static const Defaults defaults; // both a list of parameters and a list of default values static wsrep_cap_t capabilities(int protocol_version); // Return the global seqno of the last transaction which has // commmitted. // // galera-bugs#555: Assign last seen from apply_monitor_.last_left() // because apply monitor is held until the whole transaction is over. // Commit monitor may be released early due to group commit. wsrep_seqno_t last_committed() { return apply_monitor_.last_left(); } void report_last_committed(wsrep_seqno_t purge_seqno) { if (gu_unlikely(purge_seqno != -1)) { service_thd_.report_last_committed( std::min(purge_seqno, last_committed())); } } // Helpers for configuration change processing void drain_monitors_for_local_conf_change(); void process_non_prim_conf_change(void* recv_ctx, const gcs_act_cchange&, int my_index); bool skip_prim_conf_change(const wsrep_view_info_t& view, int group_proto_ver); void process_first_view(const wsrep_view_info_t*, const wsrep_uuid_t&); void process_group_change(const wsrep_view_info_t*); void process_st_required(void* recv_ctx, int group_proto_ver, const wsrep_view_info_t*); void reset_index_if_needed(const wsrep_view_info_t* view_info, int prev_protocol_version, int next_protocol_version, bool st_required); void shift_to_next_state(Replicator::State next_state); void become_joined_if_needed(); void submit_ordered_view_info(void* recv_ctx, const wsrep_view_info_t*); void finish_local_prim_conf_change(int group_proto_ver, wsrep_seqno_t seqno, const char* context); void process_prim_conf_change(void* recv_ctx, const gcs_act_cchange&, int my_index, void* cc_buf); void process_ist_conf_change(const gcs_act_cchange&); TrxHandleSlavePtr get_real_ts_with_gcache_buffer(const TrxHandleSlavePtr&); void handle_trx_overlapping_ist(const TrxHandleSlavePtr& ts); // Helpers for IST processing. void handle_ist_nbo(const TrxHandleSlavePtr& ts, bool must_apply, bool preload); void handle_ist_trx_preload(const TrxHandleSlavePtr& ts, bool must_apply); void handle_ist_trx(const TrxHandleSlavePtr& ts, bool must_apply, bool preload); /* process pending queue events scheduled before local_seqno */ void process_pending_queue(wsrep_seqno_t local_seqno); // Enter local monitor. Return true if entered. bool enter_local_monitor_for_cert(TrxHandleMaster*, const TrxHandleSlavePtr&); wsrep_status_t handle_local_monitor_interrupted(TrxHandleMaster*, const TrxHandleSlavePtr&); wsrep_status_t finish_cert(TrxHandleMaster*, const TrxHandleSlavePtr&); wsrep_status_t cert (TrxHandleMaster*, const TrxHandleSlavePtr&); wsrep_status_t cert_and_catch (TrxHandleMaster*, const TrxHandleSlavePtr&); // Enter apply monitor for local transaction. Return true // if apply monitor was grabbed. bool enter_apply_monitor_for_local(TrxHandleMaster&, const TrxHandleSlavePtr&); wsrep_status_t handle_apply_monitor_interrupted(TrxHandleMaster&, const TrxHandleSlavePtr&); void enter_apply_monitor_for_local_not_committing( const TrxHandleMaster&, TrxHandleSlave&); wsrep_status_t handle_commit_interrupt(TrxHandleMaster&, const TrxHandleSlave&); void update_state_uuid (const wsrep_uuid_t& u); void update_incoming_list (const wsrep_view_info_t& v); /* aborts/exits the program in a clean way */ void abort() GU_NORETURN; #ifdef GALERA_MONITOR_DEBUG_PRINT public: #endif /* GALERA_MONITOR_DEBUG_PRINT */ class LocalOrder { public: explicit LocalOrder(const TrxHandleSlave& ts) : seqno_(ts.local_seqno()) #if defined(GU_DBUG_ON) || !defined(NDEBUG) ,trx_(&ts) #endif //GU_DBUG_ON { } LocalOrder(wsrep_seqno_t seqno, const TrxHandleSlave* ts = NULL) : seqno_(seqno) #if defined(GU_DBUG_ON) || !defined(NDEBUG) ,trx_(ts) #endif //GU_DBUG_ON { #if defined(GU_DBUG_ON) || !defined(NDEBUG) assert((trx_ && seqno_ == trx_->local_seqno()) || !trx_); #endif //GU_DBUG_ON } wsrep_seqno_t seqno() const { return seqno_; } bool condition(wsrep_seqno_t last_entered, wsrep_seqno_t last_left) const { return (last_left + 1 == seqno_); } #ifdef GU_DBUG_ON void debug_sync(gu::Mutex& mutex) { if (trx_) { if (trx_->local()) { mutex.unlock(); GU_DBUG_SYNC_WAIT("local_monitor_master_enter_sync"); mutex.lock(); } else { mutex.unlock(); GU_DBUG_SYNC_WAIT("local_monitor_slave_enter_sync"); mutex.lock(); } } else { mutex.unlock(); GU_DBUG_SYNC_WAIT("local_monitor_enter_sync"); mutex.lock(); } } #endif //GU_DBUG_ON #ifndef NDEBUG LocalOrder() : seqno_(WSREP_SEQNO_UNDEFINED) #if defined(GU_DBUG_ON) || !defined(NDEBUG) ,trx_(NULL) #endif /* GU_DBUG_ON || !NDEBUG */ {} #endif /* NDEBUG */ void print(std::ostream& os) const { os << seqno_; } private: #ifdef NDEBUG LocalOrder(const LocalOrder& o); #endif /* NDEBUG */ wsrep_seqno_t const seqno_; #if defined(GU_DBUG_ON) || !defined(NDEBUG) // this pointer is for debugging purposes only and // is not guaranteed to point at a valid location const TrxHandleSlave* const trx_; #endif /* GU_DBUG_ON || !NDEBUG */ }; class ApplyOrder { public: ApplyOrder(const TrxHandleSlave& ts) : global_seqno_ (ts.global_seqno()), depends_seqno_(ts.depends_seqno()), is_local_ (ts.local()), is_toi_ (ts.is_toi()) #ifndef NDEBUG ,trx_ (&ts) #endif { #ifndef NDEBUG (void)trx_; // to pacify clang's -Wunused-private-field #endif } ApplyOrder(wsrep_seqno_t gs, wsrep_seqno_t ds, bool l = false) : global_seqno_ (gs), depends_seqno_(ds), is_local_ (l), is_toi_ (false) #ifndef NDEBUG ,trx_ (NULL) #endif { } wsrep_seqno_t seqno() const { return global_seqno_; } bool condition(wsrep_seqno_t last_entered, wsrep_seqno_t last_left) const { return ((is_local_ == true && is_toi_ == false) || last_left >= depends_seqno_); } #ifdef GU_DBUG_ON void debug_sync(gu::Mutex& mutex) { if (is_local_) { mutex.unlock(); GU_DBUG_SYNC_WAIT("apply_monitor_master_enter_sync"); mutex.lock(); } else { mutex.unlock(); GU_DBUG_SYNC_WAIT("apply_monitor_slave_enter_sync"); mutex.lock(); } } #endif //GU_DBUG_ON #ifndef NDEBUG ApplyOrder() : global_seqno_ (WSREP_SEQNO_UNDEFINED), depends_seqno_(WSREP_SEQNO_UNDEFINED), is_local_ (false), is_toi_ (false), trx_ (NULL) {} #endif /* NDEBUG */ void print(std::ostream& os) const { os << "g:" << global_seqno_ << " d:" << depends_seqno_ << (is_local_ ? " L" : " R"); } private: #ifdef NDEBUG ApplyOrder(const ApplyOrder&); #endif /* NDEBUG */ const wsrep_seqno_t global_seqno_; const wsrep_seqno_t depends_seqno_; const bool is_local_; const bool is_toi_; #ifndef NDEBUG // this pointer is for debugging purposes only and // is not guaranteed to point at a valid location const TrxHandleSlave* const trx_; #endif }; class CommitOrder { public: typedef enum { BYPASS = 0, OOOC = 1, LOCAL_OOOC = 2, NO_OOOC = 3 } Mode; static Mode from_string(const std::string& str) { int ret(gu::from_string(str)); switch (ret) { case BYPASS: case OOOC: case LOCAL_OOOC: case NO_OOOC: break; default: gu_throw_error(EINVAL) << "invalid value " << str << " for commit order mode"; } return static_cast(ret); } CommitOrder(const TrxHandleSlave& ts, Mode mode) : global_seqno_(ts.global_seqno()), mode_(mode), is_local_(ts.local()) #ifndef NDEBUG ,trx_(&ts) #endif { } CommitOrder(wsrep_seqno_t gs, Mode mode, bool local = false) : global_seqno_(gs), mode_(mode), is_local_(local) #ifndef NDEBUG ,trx_(NULL) #endif { } wsrep_seqno_t seqno() const { return global_seqno_; } bool condition(wsrep_seqno_t last_entered, wsrep_seqno_t last_left) const { switch (mode_) { case BYPASS: gu_throw_fatal << "commit order condition called in bypass mode"; case OOOC: return true; case LOCAL_OOOC: return is_local_; // in case of remote trx fall through case NO_OOOC: return (last_left + 1 == global_seqno_); } gu_throw_fatal << "invalid commit mode value " << mode_; } #ifdef GU_DBUG_ON void debug_sync(gu::Mutex& mutex) { if (is_local_ == true) { mutex.unlock(); GU_DBUG_SYNC_WAIT("commit_monitor_master_enter_sync"); mutex.lock(); } else { mutex.unlock(); GU_DBUG_SYNC_WAIT("commit_monitor_slave_enter_sync"); mutex.lock(); } } #endif //GU_DBUG_ON #ifndef NDEBUG CommitOrder() : global_seqno_ (WSREP_SEQNO_UNDEFINED), mode_ (OOOC), is_local_ (false), trx_ (NULL) { (void)trx_; // to pacify clang's -Wunused-private-field } #endif /* NDEBUG */ void print(std::ostream& os) const { os << "g:" << global_seqno_ << " m:" << mode_ << (is_local_ ? " L" : " R"); } private: #ifdef NDEBUG CommitOrder(const CommitOrder&); #endif const wsrep_seqno_t global_seqno_; const Mode mode_; const bool is_local_; #ifndef NDEBUG // this pointer is for debugging purposes only and // is not guaranteed to point at a valid location const TrxHandleSlave* const trx_; #endif }; private: // state machine class Transition { public: Transition(State const from, State const to) : from_(from), to_(to) { } State from() const { return from_; } State to() const { return to_; } bool operator==(Transition const& other) const { return (from_ == other.from_ && to_ == other.to_); } class Hash { public: size_t operator()(Transition const& tr) const { return (gu::HashValue(static_cast(tr.from_)) ^ gu::HashValue(static_cast(tr.to_))); } }; private: State from_; State to_; }; void build_stats_vars (std::vector& stats); void cancel_seqno(wsrep_seqno_t); void set_initial_position(const wsrep_uuid_t&, wsrep_seqno_t); void establish_protocol_versions (int version); /* * Record cc_seqno_ and cc_lowest_trx_seqno_ for future IST * processing. * * @param cc_seqno Seqno of current configuration change. * @param source String describing the source of the configuration * change. */ void record_cc_seqnos(wsrep_seqno_t cc_seqno, const char* source); bool state_transfer_required(const wsrep_view_info_t& view_info, int group_proto_ver, bool rejoined); void prepare_for_IST (void*& req, ssize_t& req_len, int group_proto_ver, int str_proto_ver, const wsrep_uuid_t& group_uuid, wsrep_seqno_t group_seqno); void recv_IST(void* recv_ctx); void process_IST_writeset(void* recv_ctx, const TrxHandleSlavePtr& ts); StateRequest* prepare_state_request (const void* sst_req, ssize_t sst_req_len, int group_proto_ver, int str_proto_ver, const wsrep_uuid_t& group_uuid, wsrep_seqno_t group_seqno); void send_state_request (const StateRequest* req, int str_proto_ver); void request_state_transfer (void* recv_ctx, int group_proto_ver, const wsrep_uuid_t& group_uuid, wsrep_seqno_t group_seqno, const void* sst_req, ssize_t sst_req_len); /* resume reception of GCS events */ void resume_recv() { gcs_.resume_recv(); ist_end(ist::Result{0, ""}); } /* These methods facilitate closing procedure. * They must be called under closing_mutex_ lock */ void start_closing(); void shift_to_CLOSED(); void wait_for_CLOSED(gu::Lock&); wsrep_seqno_t donate_sst(void* recv_ctx, const StateRequest& streq, const wsrep_gtid_t& state_id, bool bypass); /* Wait until NBO end criteria is met */ wsrep_status_t wait_nbo_end(TrxHandleMaster*, wsrep_trx_meta_t*); class InitLib /* Library initialization routines */ { public: InitLib (gu_log_cb_t cb) { gu_init(cb); } }; InitLib init_lib_; gu::Config config_; InitConfig init_config_; // registers configurable parameters and defaults struct ParseOptions { ParseOptions(Replicator& repl, gu::Config&, const char* opts); } parse_options_; // parse option string supplied on initialization class InitSSL { public: InitSSL(gu::Config& conf) { gu::ssl_init_options(conf); } } init_ssl_; // initialize global SSL parameters static int const MAX_PROTO_VER; /* * |--------------------------------------------------------------------| * | protocol_version_ | trx version | str_proto_ver | record_set_ver_ | * |--------------------------------------------------------------------| * | 1 | 1 | 0 | 1 | * | 2 | 1 | 1 | 1 | * | 3 | 2 | 1 | 1 | * | 4 | 2 | v2.1 1 | 1 | * | 5 | 3 | 1 | 1 | * | 6 | 3 | 2 | 1 | * | 7 | 3 | 2 | 1 | * | 8 | 3 | 2 | alignment 2 | * | 9 | SS keys 4 | 2 | 2 | * | 4.x 10 | PA range/ 5 | CC events / 3 | 2 | * | | UPD keys | idx preload | | * | 11 | SRV keys 6 | 3 | 2 | * |--------------------------------------------------------------------| * * Note: str_proto_ver is decided in replicator_str.cpp based on * given protocol version. */ /* last protocol version of Galera 3 series */ static int const PROTO_VER_GALERA_3_MAX = 9; /* repl protocol version which orders CC */ static int const PROTO_VER_ORDERED_CC = 10; int protocol_version_;// general repl layer proto int proto_max_; // maximum allowed proto version FSM state_; gu::Mutex closing_mutex_; // to sync close() call gu::Cond closing_cond_; bool closing_; // to indicate that the closing process // started SstState sst_state_; // configurable params const CommitOrder::Mode co_mode_; // commit order mode // persistent data location std::string state_file_; SavedState st_; // boolean telling if the node is safe to use for bootstrapping // a new primary component bool safe_to_bootstrap_; // currently installed trx parameters TrxHandleMaster::Params trx_params_; // identifiers wsrep_uuid_t uuid_; wsrep_uuid_t const state_uuid_; const char state_uuid_str_[37]; wsrep_seqno_t cc_seqno_; // seqno of last CC // Lowest trx seqno in cert index during last CC wsrep_seqno_t cc_lowest_trx_seqno_; wsrep_seqno_t pause_seqno_; // local seqno of last pause call // application callbacks void* app_ctx_; wsrep_connected_cb_t connected_cb_; wsrep_view_cb_t view_cb_; wsrep_sst_request_cb_t sst_request_cb_; wsrep_apply_cb_t apply_cb_; wsrep_unordered_cb_t unordered_cb_; wsrep_sst_donate_cb_t sst_donate_cb_; wsrep_synced_cb_t synced_cb_; // SST std::string sst_donor_; wsrep_uuid_t sst_uuid_; wsrep_seqno_t sst_seqno_; gu::Mutex sst_mutex_; gu::Cond sst_cond_; int sst_retry_sec_; bool sst_received_; // services ProgressCallback gcache_progress_cb_; gcache::GCache gcache_; ProgressCallback joined_progress_cb_; GCS_IMPL gcs_; ServiceThd service_thd_; // action sources TrxHandleSlave::Pool slave_pool_; ActionSource* as_; ProgressCallbackist_progress_cb_; ist::Receiver ist_receiver_; ist::AsyncSenderMap ist_senders_; // trx processing Wsdb wsdb_; Certification cert_; class PendingCertQueue { public: PendingCertQueue(gcache::GCache& gcache) : mutex_(), ts_queue_(), gcache_(gcache) { } void push(const TrxHandleSlavePtr& ts) { assert(ts->local()); assert(ts->local_seqno() > 0); gu::Lock lock(mutex_); ts_queue_.push(ts); ts->mark_queued(); } TrxHandleSlavePtr must_cert_next(wsrep_seqno_t seqno) { gu::Lock lock(mutex_); TrxHandleSlavePtr ret; if (!ts_queue_.empty()) { const TrxHandleSlavePtr& top(ts_queue_.top()); assert(top->local_seqno() != seqno); if (top->local_seqno() < seqno) { ret = top; ts_queue_.pop(); } } return ret; } void clear() { gu::Lock lock(mutex_); while (not ts_queue_.empty()) { TrxHandleSlavePtr ts(ts_queue_.top()); ts_queue_.pop(); gcache_.free(const_cast(ts->action().first)); } } private: struct TrxHandleSlavePtrCmpLocalSeqno { bool operator()(const TrxHandleSlavePtr& lhs, const TrxHandleSlavePtr& rhs) const { return lhs->local_seqno() > rhs->local_seqno(); } }; gu::Mutex mutex_; std::priority_queue, TrxHandleSlavePtrCmpLocalSeqno> ts_queue_; gcache::GCache& gcache_; }; PendingCertQueue pending_cert_queue_; // concurrency control Monitor local_monitor_; Monitor apply_monitor_; Monitor commit_monitor_; gu::datetime::Period causal_read_timeout_; // counters gu::Atomic receivers_; gu::Atomic replicated_; gu::Atomic replicated_bytes_; gu::Atomic keys_count_; gu::Atomic keys_bytes_; gu::Atomic data_bytes_; gu::Atomic unrd_bytes_; gu::Atomic local_commits_; gu::Atomic local_rollbacks_; gu::Atomic local_cert_failures_; gu::Atomic local_replays_; gu::Atomic causal_reads_; gu::Atomic preordered_id_; // temporary preordered ID // non-atomic stats std::string incoming_list_; mutable gu::Mutex incoming_mutex_; mutable std::vector wsrep_stats_; }; std::ostream& operator<<(std::ostream& os, ReplicatorSMM::State state); #ifdef GALERA_MONITOR_DEBUG_PRINT inline std::ostream& operator<<(std::ostream& os,const ReplicatorSMM::LocalOrder& o) { o.print(os); return os; } inline std::ostream& operator<<(std::ostream& os,const ReplicatorSMM::ApplyOrder& o) { o.print(os); return os; } inline std::ostream& operator<<(std::ostream& os,const ReplicatorSMM::CommitOrder& o) { o.print(os); return os; } #endif /* GALERA_MONITOR_DEBUG_PRINT */ /** * Get transaction protocol and record set versions based on group protocol * * @param proto_ver Group protocol version * * @return Tuple consisting of trx protocol and record set versions. */ std::tuple get_trx_protocol_versions(int proto_ver); } /* namespace galera */ #endif /* GALERA_REPLICATOR_SMM_HPP */ galera-4-26.4.22/galera/SConscript000644 000162 177776 00000000064 14755062442 017757 0ustar00jenkinsnogroup000000 000000 SConscript(['src/SConscript', 'tests/SConscript']) galera-4-26.4.22/man/000755 000162 177776 00000000000 14755062445 015270 5ustar00jenkinsnogroup000000 000000 galera-4-26.4.22/man/garbd.8000644 000162 177776 00000004353 14755062442 016442 0ustar00jenkinsnogroup000000 000000 .TH GARBD "8" "December 2014" "garbd INFO: 2.8.r165" "System Administration Utilities" .SH NAME garbd \- arbitrator daemon for Galera cluster .SH SYNOPSIS .B garbd [\fI\,options\/\fR] [\fI\,group address\/\fR] .SH DESCRIPTION .B garbd joins Galera cluster as an additional node for the purpose of establishing quorum in case of network partitioning. It can do so by serving: .RS a) as an odd node to prevent split-brains; .RE .RS b) as a reference connection point outside a datacenter. .RE Arbitrator node must see all messages that the other nodes of the cluster see, however it does not process them any further and just discards them. As such it does not store any cluster state and can't be used to bootstrap the cluster, so it only can join existing cluster. .SH OPTIONS .SS "Configuration:" .TP \fB\-d\fR [ \fB\-\-daemon\fR ] Become daemon .TP \fB\-a\fR [ \fB\-\-address\fR ] arg Group address in Galera format .TP \fB\-g\fR [ \fB\-\-group\fR ] arg Group name .TP \fB\-\-sst\fR arg SST request string that contains SST request to trigger state snapshot dump (state backup) on one of the other nodes. For details refer to Galera documentation at https://www.galeracluster.com .TP \fB\-\-donor\fR arg SST donor name (for state dump) .TP \fB\-o\fR [ \fB\-\-options\fR ] arg GCS/GCOMM option list. It is likely to be the same as on other nodes of the cluster. .TP \fB\-l\fR [ \fB\-\-log\fR ] arg Path to log file .TP \fB\-c\fR [ \fB\-\-cfg\fR ] arg Path to configuration file. Configuration file contains garbd options in the form \fB