Ejemplo n.º 1
0
Archivo: db.go Proyecto: badoo/thunder
func Setup(dsn string, dbMaxConns uint32) error {
	var err error

	db, err = sql.Open("mysql", dsn)
	if err != nil {
		return err
	}

	db.SetMaxOpenConns(int(dbMaxConns))

	parts := strings.Split(dsn, ")")
	if len(parts) != 2 {
		log.Fatalf("Unexpected MySQL DSN format, expected it to in form '<login>:<password>@<transport>(...)/<db>'")
	}

	parts = strings.Split(parts[1], "/")
	if len(parts) != 2 {
		log.Fatalf("Unexpected MySQL DSN format, expected it to be in form of '<host_settings>/<db>'")
	}

	qParts := strings.Split(parts[1], "?")
	dbName = qParts[0]

	return nil
}
Ejemplo n.º 2
0
func GenerateJobsCycle() {
	hostname, err := os.Hostname()
	if err != nil {
		log.Fatalf("Could not get hostname: %s", err.Error())
	}

	log.Print("Initial select from RunQueue and starting launcher goroutines")
	if err := selectRQAndNotify(); err != nil {
		log.Fatalf("Could not do initial select run queue: %s", err.Error())
	}

	log.Print("Initial select from Timetable")
	ttRows, err := selectTimetable()
	if err != nil {
		log.Fatalf("Could not do initial select timetable: %s", err.Error())
	}

	log.Print("Starting jobgen goroutines")
	if err := notifyForFullTTSelect(ttRows, false); err != nil {
		log.Fatalf("Could notify about timetable: %s", err.Error())
	}

	for {
		res, err := db.LockCycle(getLockName(), hostname)
		if err != nil || !res {
			if err == nil {
				log.Println("Could not get lock, another host holds it? Retrying in 10 seconds")
			} else {
				log.Warningf("Could not get lock, got DB error: ", err.Error())
			}

			time.Sleep(time.Second * 10)
			continue
		}

		// timer := pinba.TimerStart(map[string]string{"group": "jobgenerator"})
		startTs := time.Now().UnixNano()

		db.LogCycleStart(CYCLE_CLASS_NAME, hostname, 0)
		log.Debug("Cycle started")
		success := doCycle()
		log.Debug("Cycle finished")
		successInt := 1
		if !success {
			successInt = 0
		}
		db.LogCycleStop(CYCLE_CLASS_NAME, hostname, 0, successInt)

		passedMs := int64((time.Now().UnixNano() - startTs) / 1e6)

		if passedMs < cycleMs {
			time.Sleep(time.Duration(cycleMs-passedMs) * time.Millisecond)
		}
	}
}
Ejemplo n.º 3
0
func Setup(config common.FullConfig) {
	isDevelServer = config.GetIsDevel()

	dispatchThreads.v = make(map[string]map[string]*DispatcherData)
	launcherThreads.v = make(map[string]*LauncherData)
	killerThreads.v = make(map[string]map[string]bool)

	throttle.c = make(chan bool, THROTTLE_CHAN_CAPACITY)
	throttle.setIntervalCh = make(chan time.Duration, 1)
	go throttleThread()

	rusageInfo.groupsMaxParrots = make(map[string]uint64)
	rusageInfo.groupHosts = make(map[string][]string)
	rusageInfo.hostsInfo = make(map[string]*ServerInfo)
	rusageInfo.loadEstimate = make(map[string]*ScriptRusageEntry)
	rusageInfo.timetableRusage = make(map[uint64]*ScriptRusageEntry)
	rusageInfo.groupIdx = make(map[string]uint64)

	def := config.GetDefault()

	defaultParasiteMemory = def.GetParasiteMemory()
	defaultMinIdleCpu = def.GetMinIdleCpu()
	defaultMinMemory = def.GetMinMemory()
	defaultMinMemoryRatio = def.GetMinMemoryRatio()
	defaultMaxMemory = def.GetMaxMemory()
	defaultRusage = def.GetRusage()

	cycleMs = config.GetCycleMs()
	ttReloadIntervalMs = config.GetFullTimetableReloadIntervalMs()
	autoIncrementIncrement = getAutoIncrementIncrement()

	launcherConf := config.GetLauncher()
	hostSuffix = launcherConf.GetHostSuffix()
	basePath = launcherConf.GetBasePath()
	if launcherConf.DeveloperPath != nil {
		haveDeveloper = true
		developerPath = launcherConf.GetDeveloperPath()
		log.Printf("We have developer dir: %s", developerPath)
	}

	log.Printf("Updating hosts")

	updateHosts()

	log.Printf("Launching update hosts thread")

	go updateHostsThread()

	log.Printf("Clearing old heartbeats")

	if err := clearOldHeartbeats(); err != nil {
		log.Fatalf("Could not clear old heartbeats: %s", err.Error())
	}

	log.Printf("Launching periodic run queue select thread")

	go runqueuePeriodicSelectThread()
	go forceCheckDeletedThread()
}
Ejemplo n.º 4
0
func Initialize(default_config_path string, service_conf Config) {
	flag.StringVar(&flags.ConfFile, "c", default_config_path, "path to config file")
	flag.StringVar(&flags.LogFile, "l", "", "path to log file, special value '-' means 'stdout'")
	flag.StringVar(&flags.PidFile, "p", "", "path to pid file. if empty, pidfile will not be created")
	flag.BoolVar(&flags.Testconf, "t", false, "test configuration and exit")
	flag.BoolVar(&flags.Version, "v", false, "print version")
	flag.BoolVar(&flags.FullVersion, "V", false, "print full version info")
	flag.BoolVar(&flags.Debug, "debug", false, "force DEBUG log level")
	flag.Parse()

	if flags.Version {
		fmt.Printf("%s\n", VersionInfo.GetVersion())
		os.Exit(0)
	}

	if flags.FullVersion {
		data, _ := json.MarshalIndent(VersionInfo, "", "  ")
		fmt.Printf("%s\n", data)
		os.Exit(0)
	}

	var err error

	config = service_conf                    // save a pointer to service's config (NOT a copy, mon!)
	commandLine = strings.Join(os.Args, " ") // XXX(antoxa): couldn't think of a better way
	hostname = getHostname()                 // get hostname early

	// moved here from init(), just importing a package should not publish expvars
	initExpvars()

	// current executable full path (symlinks and shit sometimes complicate things)
	binaryPath = func() string {
		path, err := osutil.GetCurrentBinary()
		if err != nil {
			// log as notice, non-critical error (only stats affected)
			log.Infof("couldn't get current binary (using argv[0] = %q): %v", os.Args[0], err)
			return os.Args[0]
		}
		return path
	}()

	// config path
	confPath := func() string {
		if flags.ConfFile != "" {
			return flags.ConfFile
		}
		return default_config_path
	}()

	// resolve absolute config path, convenient for stats
	configPath = func(path string) string {
		var err error
		if path, err = filepath.Abs(path); err != nil {
			return path
		}
		if path, err = filepath.EvalSymlinks(path); err != nil {
			return path
		}
		return path
	}(confPath)

	// parse config and construct final config merged with command line flags
	// use path as supplied to us in args (i.e. unresolved), just to avoid 'too smart, outsmarted yourself' gotchas
	err = ParseConfigFromFile(confPath, service_conf)
	if err != nil {
		err_message := func(err error) string {
			switch real_err := err.(type) {
			case nil:
				return "syntax is ok"
			case *json.SyntaxError:
				return fmt.Sprintf("%v at offset %d", real_err, real_err.Offset)
			case *os.PathError:
				return fmt.Sprintf("%v", real_err)
			default:
				return fmt.Sprintf("(%T) %v", real_err, real_err)
			}
		}(err)

		stderrLogger.Fatalf("Error in config: %s", err_message)

	} else {
		if flags.Testconf {
			fmt.Printf("testconf %s: syntax is ok\n", configPath)
		}
	}

	mergeCommandlineFlagsToConfig(flags, config)

	daemonConfig := config.GetDaemonConfig()

	// antoxa: need the fancy wrapper function to have testconf behave properly
	// FIXME: testconf should check more stuff (below) and graceful restart also
	initPidfileLogfile := func() error {

		// FIXME(antoxa): this testconf thingy is everywhere! must... resist... full rewrite
		if flags.Testconf {
			err = PidfileTest(daemonConfig.GetPidFile())
		} else {
			pidfile, err = PidfileOpen(daemonConfig.GetPidFile())
		}

		if err != nil {
			return fmt.Errorf("can't open pidfile: %s", err)
		}

		// FIXME: this is shit ugly
		//  need better integration between logger and daemon-config
		//  or 1-to-1 mapping
		//  or better log package :)
		log_level := daemonConfig.GetLogLevel()
		if log_level == 0 {
			return fmt.Errorf("unknown log_level, supported: %v", badoo_config.ServiceConfigDaemonConfigTLogLevels_name)
		}
		err = reopenLogfile(daemonConfig.GetLogFile(), log.Level(log_level))
		if err != nil {
			return fmt.Errorf("can't open logfile: %s", err)
		}

		return nil
	}

	err = initPidfileLogfile()
	if err != nil {
		if flags.Testconf {
			stderrLogger.Errorf("%v", err)
			fmt.Printf("testconf failed\n")
		} else {
			stderrLogger.Errorf("%v", err) // always pidfile/logfile errors to stderr
		}
		os.Exit(1)
	} else {
		if flags.Testconf {
			fmt.Printf("testconf successful\n")
			os.Exit(0)
		}
	}

	// log some version info like libangel does
	versionString := func() string {
		vi := &VersionInfo
		version := func() string {
			if vi.GetAutoBuildTag() != "" {
				return fmt.Sprintf("%s-%s", vi.GetVersion(), vi.GetAutoBuildTag())
			} else {
				return vi.GetVersion()
			}
		}()
		return fmt.Sprintf("%s version %s, git %s, built %s on %s",
			vi.GetVcsBasename(), version, vi.GetVcsShortHash(), vi.GetBuildDate(), vi.GetBuildHost())
	}()
	log.Infof("%s", versionString)

	// max cpus, 0 = all of them
	numCPU := func() int {
		maxCpus := int(daemonConfig.GetMaxCpus())
		if maxCpus <= 0 || maxCpus > runtime.NumCPU() {
			maxCpus = runtime.NumCPU()
		}
		return maxCpus
	}()
	runtime.GOMAXPROCS(numCPU)

	// gc percent, <0 - disables GC
	if daemonConfig.GcPercent != nil {
		debug.SetGCPercent(int(daemonConfig.GetGcPercent()))
	}

	// process pinba configuration and related stuff
	pinbaSender, err = func() (*PinbaSender, error) { // assigns a global
		if daemonConfig.GetPinbaAddress() == "" {
			return nil, nil // user doesn't want pinba configured
		}

		pi, err := PinbaInfoFromConfig(config)
		if err != nil {
			return nil, err
		}

		return NewPinbaSender(pi), nil
	}()

	if err != nil {
		log.Fatalf("pinba config error: %v", err)
	}

	// graceful restart handling
	//  see restart.go and signals.go for more details
	restartData, err = ParseRestartDataFromEnv()
	if err != nil {
		log.Fatalf("can't parse restart data: %v", err)
	}
	if restartData != nil {
		log.Debugf("[CHILD] this is a restart, parent: %d, me: %d", restartData.PPid, os.Getpid())
	}

	// start http pprof server (possibly - inherit fd from parent if this is a restart)
	err = func() (err error) {
		HttpServer, err = newHttpServer(config, restartData) // assigning a global here
		if err != nil {
			return err
		}

		if HttpServer != nil { // nil here means it has not been configured
			go HttpServer.Serve()
		}

		return nil
	}()
	if err != nil {
		log.Fatalf("can't start http_pprof server: %v", err)
	}
}