// forceMasterSnapshot: Normally a master is not a viable tablet to snapshot. // However, there are degenerate cases where you need to override this, for // instance the initial clone of a new master. func (wr *Wrangler) PartialSnapshot(tabletAlias topo.TabletAlias, keyName string, startKey, endKey key.HexKeyspaceId, forceMasterSnapshot bool, concurrency int) (manifest string, parent topo.TabletAlias, err error) { restoreAfterSnapshot, err := wr.prepareToSnapshot(tabletAlias, forceMasterSnapshot) if err != nil { return } defer func() { err = replaceError(err, restoreAfterSnapshot()) }() actionPath, err := wr.ai.PartialSnapshot(tabletAlias, &tm.PartialSnapshotArgs{keyName, startKey, endKey, concurrency}) if err != nil { return } results, actionErr := wr.ai.WaitForCompletionReply(actionPath, wr.actionTimeout()) var reply *tm.SnapshotReply if actionErr != nil { relog.Error("PartialSnapshot failed, still restoring tablet type: %v", actionErr) reply = &tm.SnapshotReply{} } else { reply = results.(*tm.SnapshotReply) tm.BackfillAlias(reply.ZkParentPath, &reply.ParentAlias) } return reply.ManifestPath, reply.ParentAlias, actionErr }
// forceMasterSnapshot: Normally a master is not a viable tablet to snapshot. // However, there are degenerate cases where you need to override this, for // instance the initial clone of a new master. func (wr *Wrangler) Snapshot(tabletAlias topo.TabletAlias, forceMasterSnapshot bool, snapshotConcurrency int, serverMode bool) (manifest string, parent topo.TabletAlias, slaveStartRequired, readOnly bool, originalType topo.TabletType, err error) { var ti *topo.TabletInfo ti, err = wr.ts.GetTablet(tabletAlias) if err != nil { return } originalType = ti.Tablet.Type if ti.Tablet.Type == topo.TYPE_MASTER && forceMasterSnapshot { // In this case, we don't bother recomputing the serving graph. // All queries will have to fail anyway. log.Infof("force change type master -> backup: %v", tabletAlias) // There is a legitimate reason to force in the case of a single // master. ti.Tablet.Type = topo.TYPE_BACKUP err = topo.UpdateTablet(wr.ts, ti) } else { err = wr.ChangeType(ti.Alias(), topo.TYPE_BACKUP, false) } if err != nil { return } var actionPath string actionPath, err = wr.ai.Snapshot(tabletAlias, &tm.SnapshotArgs{snapshotConcurrency, serverMode}) if err != nil { return } // wait for completion, and save the error results, actionErr := wr.ai.WaitForCompletionReply(actionPath, wr.actionTimeout()) var reply *tm.SnapshotReply newType := originalType if actionErr != nil { log.Errorf("snapshot failed, still restoring tablet type: %v", actionErr) reply = &tm.SnapshotReply{} } else { reply = results.(*tm.SnapshotReply) tm.BackfillAlias(reply.ZkParentPath, &reply.ParentAlias) if serverMode { log.Infof("server mode specified, switching tablet to snapshot_source mode") newType = topo.TYPE_SNAPSHOT_SOURCE } } // Go back to original type, or go to SNAPSHOT_SOURCE log.Infof("change type after snapshot: %v %v", tabletAlias, newType) if ti.Tablet.Parent.Uid == topo.NO_TABLET && forceMasterSnapshot && newType != topo.TYPE_SNAPSHOT_SOURCE { log.Infof("force change type backup -> master: %v", tabletAlias) ti.Tablet.Type = topo.TYPE_MASTER err = topo.UpdateTablet(wr.ts, ti) } else { err = wr.ChangeType(ti.Alias(), newType, false) } if err != nil { // failure in changing the topology type is probably worse, // so returning that (we logged actionErr anyway) return } return reply.ManifestPath, reply.ParentAlias, reply.SlaveStartRequired, reply.ReadOnly, originalType, actionErr }