Mongo ReplicaSet instance support - #244
Conversation
| for _, n := range dataNodes { | ||
| hostParts = append(hostParts, fmt.Sprintf("%s:%d", n.hostname, port)) | ||
| } | ||
| uri := fmt.Sprintf("mongodb://%s:%s@%s/%s?replicaSet=%s&authSource=admin", |
There was a problem hiding this comment.
Can we URL-encode username/password in Mongo URI. Special chars like @ or : can break authentication.
| haConfig := database.Spec.Instance.HAConfig | ||
| port := haConnectivityManagers[common.DATABASE_TYPE_MONGODB].PrimaryPort(haConfig) | ||
| rsName := haConfig.MongoDB.ReplicaSetName | ||
| dbName := strings.Join(database.Spec.Instance.DatabaseNames, ",") |
There was a problem hiding this comment.
Mongo URI path should have only one DB name. Comma-joined names can cause auth failure.
| // Query NDB for the current set of database nodes (hostname + IP). | ||
| // This is a targeted GET on the cluster ID — the same ID that was set when | ||
| // the provisioning operation completed and stored in database.Status.Id. | ||
| dbResponse, err := ndb_api.GetDatabaseById(ctx, ndbClient, database.Status.Id) |
There was a problem hiding this comment.
This NDB API call runs every reconcile for READY DBs. Please avoid repeated calls by reading from status/cache.
|
|
||
| // Create or reconcile a headless Service + Endpoints for each data node. | ||
| for _, n := range dataNodes { | ||
| nn := types.NamespacedName{Name: n.hostname, Namespace: database.Namespace} |
There was a problem hiding this comment.
Service name uses only hostname; this can collide across DBs in same namespace. Please add DB scoping or ownerRef check.
There was a problem hiding this comment.
the headless services must match the VM hostname used in mongoDB's rs.conf().
We can enforce a rule saying that each mongo HA DB resource should be created in its own namespace.
There was a problem hiding this comment.
We can't add DB scoping here as VM names should be just the node names and not like ..svc.local
What we can do is enforce a rule saying that each mongo HA resource should be having its own namespace.
| "restart_mongod": "true", | ||
| "working_dir": "/tmp", | ||
| "db_user": "admin", | ||
| "db_user": "mongod", |
There was a problem hiding this comment.
This may change default Mongo user globally. Please keep shared default as "admin" and override only for Mongo HA.
| // create headless services for MongoDB HA replicaset type | ||
| if database.Spec.Instance != nil && | ||
| database.Spec.Instance.HAConfig != nil && | ||
| database.Spec.Instance.HAConfig.MongoDB != nil { |
There was a problem hiding this comment.
Please use database.IsMongoHA() here for proper type check and consistency.
| for _, n := range haConfig.Nodes { | ||
| node := Node{ | ||
| VmName: n.VmName, | ||
| NxClusterId: n.ClusterId, |
There was a problem hiding this comment.
clusterName-only configs may fail since only ClusterId is used. Please resolve clusterName - clusterId or validate strictly.
There was a problem hiding this comment.
This is already handled — ResolveNamesToUUIDs in controller_adapters/name_resolution.go
this resolves clusterName to clusterId before reaching this state.
| } | ||
| } | ||
|
|
||
| if len(haConfig.Nodes) > 0 && primaryCount != 1 { |
There was a problem hiding this comment.
Please enforce minimum Mongo HA node count.
| "cluster_name": haConfig.ReplicaSetName, | ||
| "cluster_description": haConfig.ReplicaSetDescription, | ||
| // listener_port: use the configured port (may differ from 27017 if overridden in CRD) | ||
| "listener_port": strconv.Itoa(int(haConfig.MongoListenerPort)), |
There was a problem hiding this comment.
Please use PrimaryPort() fallback here too, so listener_port is never sent as 0.
| // inside the driver matches the names in rs.conf() and CoreDNS can resolve them. | ||
| // e.g.: mongodb://user:pass@mongo-rs1:27017,mongo-rs2:27017/dbname?replicaSet=rs0&authSource=admin | ||
| var hostParts []string | ||
| for _, n := range dataNodes { |
There was a problem hiding this comment.
Services/endpoints look create-only; please clean up stale ones when nodes are removed.
There was a problem hiding this comment.
Yes we ought to add the logic for this. Shall we have another PR for this change as the current PR is big.
| // When true, exactly one node with nodeType "arbiter" must be present in nodes[]. | ||
| // When false (default), no arbiter nodes may be present — all nodes are data-bearing. | ||
| // +optional | ||
| DeployArbiter bool `json:"deployArbiter,omitempty"` |
There was a problem hiding this comment.
Is deployArbiter, and listener port defaulted in web-hooks or run time code?
There was a problem hiding this comment.
Fixed, this is defaulted in webhook!
| @@ -109,6 +112,74 @@ func (v *MysqlHAParamsValidator) Validate(haConfig *InstanceHAConfig, haPath *fi | |||
| } | |||
| } | |||
|
|
|||
There was a problem hiding this comment.
What do you think about breaking down these validators into corresponding database files? e.g monogdbvalidaotr, postgres, etc. Its fine you dont think its needed, just wanted to ask.
There was a problem hiding this comment.
Oh, i need this only for HA type of database instance. Keeping them here would be fine.
|
|
||
| HA_NODE_TYPE_ARBITER = "arbiter" | ||
|
|
||
| HA_NODE_ROLE_MONGO_PRIMARY = "primary" |
There was a problem hiding this comment.
we do have an existing "primary or "secondary constant we can just use,
There was a problem hiding this comment.
Actually other DBs refer them as "Primary/Secndary" and not "primary/secondary". Capital letter difference 😄
| // A replica set needs at least 3 members to form a quorum (e.g. PSS or PSA topology). | ||
| HA_MONGO_MIN_NODE_COUNT = 3 | ||
|
|
||
| HA_NODE_TYPE_ARBITER = "arbiter" |
There was a problem hiding this comment.
why do you have HA_NODE_TYPE_ARBITER = "arbiter" and HA_NODE_ROLE_MONGO_ARBITER = "arbiter
There was a problem hiding this comment.
HA_NODE_TYPE_ARBITER variable is used for CRD spec validation when user defines mongo HA CR.
HA_NODE_ROLE_MONGO_ARBITER is used to populate field in provision API call.
keeping them distinct is better because both serve different purpose.
|
|
||
| HA_MONGO_DEFAULT_LISTENER_PORT = int32(27017) | ||
| // A replica set needs at least 3 members to form a quorum (e.g. PSS or PSA topology). | ||
| HA_MONGO_MIN_NODE_COUNT = 3 |
There was a problem hiding this comment.
can you link a doc explaining where you got this please
There was a problem hiding this comment.
i searched through online resources, we keep odd number for leader elections.
For HA cluster min odd number would be 3.
| } | ||
|
|
||
| // Database related info to be stored in the status field of the NDB CR | ||
| type NDBServerDatabaseInfo struct { |
There was a problem hiding this comment.
This class is database agnostic, we should avoid adding in database specific info here, is there another way we can do this?
There was a problem hiding this comment.
Yeah I'm storing here because most of the status fields in DB CR is fetched from NDB CR status fields.
- If we want to store in DB CR then we should have a separate NDB API call every reconcile which would make the operator feel loaded.
- We are anyways fetching IP address from NDB CR reconcile and populating it in DB CR status field. Witht eh same API call we can fetch hostnames and store here for DB reconcile logic. This field is specific for mongo HA alone and its optional.
abbfb11 to
7b6e0b2
Compare
7b6e0b2 to
ff86707
Compare
What this PR does / why we need it:
This PR is for adding support for Mongo HA (ReplicaSet type) via NDB Operator.
How Has This Been Tested?:
A mongo HA replicaset instance would be provisioned and one sample mongo-app pod would be run inside k8s cluster by using the secret connection string generated by operator.
There are three test scenarios conducted:
it excluded arbiter nodes and utilized other nodes.
database CR yaml manifest
2) Provision mongo HA cluster without arbiter node, it should create headless svc for all nodes. database cr yaml manifest
3) Provision mongo HA without arbiter node, simulate re-election and check if mongo-app is able to recognize which node is primary. This screenshot shows logging into primary node and performing re-election
Special notes for your reviewer:
Mongo ReplicaSet instance contains of primary, secondary nodes and optional arbiter node. Primary node would receive write/read traffic and secondary nodes would be used for read only traffic. There is asynchronous replication happening from primary to secondary nodes. Arbiter node is optional as it doesn't hold any actual data but its helps in leader election incase primary node goes down. Use arbiter node for smaller workloads (to save infra costs) and not for production workload.