summaryrefslogtreecommitdiff
path: root/db/repl/replset.h
blob: 6eda7e8130df3a9ae575e5358294ff0f0cbd1172 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// /db/repl/replset.h

/**
*    Copyright (C) 2008 10gen Inc.
*
*    This program is free software: you can redistribute it and/or  modify
*    it under the terms of the GNU Affero General Public License, version 3,
*    as published by the Free Software Foundation.
*
*    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 Affero General Public License for more details.
*
*    You should have received a copy of the GNU Affero General Public License
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "../../util/concurrency/list.h"
#include "../../util/concurrency/value.h"
#include "../../util/concurrency/msg.h"
#include "../../util/hostandport.h"
#include "rstime.h"
#include "rsmember.h"
#include "rs_config.h"

namespace mongo {

    extern bool replSet; // true if using repl sets
    extern class ReplSet *theReplSet; // null until initialized

    extern Tee *rsLog;

    /* information about the entire repl set, such as the various servers in the set, and their state */
    /* note: We currently do not free mem when the set goes away - it is assumed the replset is a 
             singleton and long lived.
    */
    class ReplSet {
    public:
        static enum StartupStatus { 
            PRESTART=0, LOADINGCONFIG=1, BADCONFIG=2, EMPTYCONFIG=3, 
            EMPTYUNREACHABLE=4, STARTED=5, SOON=6 } startupStatus;
        static string startupStatusMsg;

    private: 
        MemberState _myState;

    public:
        void fatal();
        bool isMaster(const char *client);
        void fillIsMaster(BSONObjBuilder&);
        bool ok() const { return _myState != FATAL; }
        MemberState state() const { return _myState; }        
        string name() const { return _name; } /* @return replica set's logical name */

        /* cfgString format is 
           replsetname/host1,host2:port,...
           where :port is optional.

           throws exception if a problem initializing. */
        ReplSet(string cfgString);

        /* call after constructing to start - returns fairly quickly after launching its threads */
        void go() { _myState = STARTUP2; startThreads(); }

        // for replSetGetStatus command
        void summarizeStatus(BSONObjBuilder&) const;
        void summarizeAsHtml(stringstream&) const;
        const ReplSetConfig& config() { return *_cfg; }

    private:
        string _name;
        const vector<HostAndPort> *_seeds;
        ReplSetConfig *_cfg;

        /** load our configuration from admin.replset.  try seed machines too. 
            throws exception if a problem.
        */
        void _loadConfigFinish(vector<ReplSetConfig>& v);
        void loadConfig();
        void initFromConfig(ReplSetConfig& c);//, bool save);

        class Consensus {
            ReplSet &rs;
            void _electSelf();
        public:
            Consensus(ReplSet *t) : rs(*t) { }
            int totalVotes() const;
            bool aMajoritySeemsToBeUp() const;
            void electSelf();
        } elect;

    public:
        struct Member : public List1<Member>::Base {
            Member(HostAndPort h, unsigned ord, const ReplSetConfig::MemberCfg *c);
            string fullName() const { return h().toString(); }
            const ReplSetConfig::MemberCfg& config() const { return *_config; }
            void summarizeAsHtml(stringstream& s) const;
            const HeartbeatInfo& hbinfo() const { return _hbinfo; }
            string lhb() { return _hbinfo.lastHeartbeatMsg; }
            MemberState state() const { return _state; }
            const HostAndPort& h() const { return _h; }
            unsigned id() const { return _hbinfo.id(); }
            friend class ReplSet;
        private:
            const ReplSetConfig::MemberCfg *_config; /* todo: when this changes??? */
            HostAndPort _h;
            MemberState _state;
            HeartbeatInfo _hbinfo;
        };
        list<HostAndPort> memberHostnames() const;
        const Member* currentPrimary() const { return _currentPrimary; }
        const ReplSetConfig::MemberCfg& myConfig() const { return _self->config(); }
        void msgUpdateHBInfo(HeartbeatInfo);

    private:
        const Member *_currentPrimary;

        static string stateAsStr(MemberState state);
        static string stateAsHtml(MemberState state);

        Member *_self;
        /* all members of the set EXCEPT self. */
        List1<Member> _members;
        Member* head() const { return _members.head(); }

        void startThreads();
        friend class FeedbackThread;

    public:
        class Manager : public task::Port {
            bool got(const any&);
            ReplSet *_rs;
            int _primary;
            const Member* findOtherPrimary();
            void noteARemoteIsPrimary(const Member *);
        public:
            Manager(ReplSet *rs);
            void msgReceivedNewConfig(BSONObj) { assert(false); }
            void msgCheckNewState();
        };
        shared_ptr<Manager> mgr;

    };

    inline void ReplSet::fatal() 
    { _myState = FATAL; log() << "replSet error fatal error, stopping replication" << rsLog; }

    inline ReplSet::Member::Member(HostAndPort h, unsigned ord, const ReplSetConfig::MemberCfg *c) : 
        _config(c), _h(h), _hbinfo(ord) { }

    inline bool ReplSet::isMaster(const char *client) {         
        /* todo replset */
        return false;
    }

}