Repository: hashicorp/nomad
File: nomad/job_endpoint.go (Go)

Code before:
			prevCounts[tg.Name] = tg.Count
		}
		for _, tg := range args.Job.TaskGroups {
			if count, ok := prevCounts[tg.Name]; ok {
				tg.Count = count
			}
		}
	}
	// Preserve the existing task resources, if requested
	if existingJob != nil && args.PreserveResources {
		prevResources := make(map[string]map[string]*structs.Resources)
		for _, tg := range existingJob.TaskGroups {
			prevResources[tg.Name] = make(map[string]*structs.Resources)
			for _, task := range tg.Tasks {
				prevResources[tg.Name][task.Name] = task.Resources
			}

		}
		for _, tg := range args.Job.TaskGroups {
			for _, task := range tg.Tasks {
				if res, ok := prevResources[tg.Name][task.Name]; ok {
					task.Resources = res
				}
			}
		}
	}

	// Submit a multiregion job to other regions (enterprise only).
	// The job will have its region interpolated.
	var newVersion uint64
	if existingJob != nil {
		newVersion = existingJob.Version + 1
	}
	isRunner, err := j.multiregionRegister(args, reply, newVersion)
	if err != nil {
		return err
	}

	// Create a new evaluation
	now := time.Now().UnixNano()
	submittedEval := false
	var eval *structs.Evaluation

	// Set the submit time
	args.Job.SubmitTime = now

	// If the job is periodic or parameterized, we don't create an eval.
	if !(args.Job.IsPeriodic() || args.Job.IsParameterized()) {

		// Initially set the eval priority to that of the job priority. If the
		// user supplied an eval priority override, we subsequently use this.

Review comment (suggestion):
I realize we're copying the existing logic for `PreserveCounts` here, but unfortunately I think that logic is racy with concurrent RPCs. Ex. supposed I have a `Job.Register` and `Job.Scale` RPC in-flight at the same time:

* `Job.Register` reads the resources
* `Job.Scale` updates the resources and writes to the FSM
* `Job.Register` writes to the FSM

So I think we need to move _both_ of these features into the state store code.

Code after:
		newVersion = existingJob.Version + 1
	}
	isRunner, err := j.multiregionRegister(args, reply, newVersion)
	if err != nil {
		return err
	}

	// Create a new evaluation
	now := time.Now().UnixNano()
	submittedEval := false
	var eval *structs.Evaluation

	// Set the submit time
	args.Job.SubmitTime = now

	// If the job is periodic or parameterized, we don't create an eval.
	if !(args.Job.IsPeriodic() || args.Job.IsParameterized()) {

		// Initially set the eval priority to that of the job priority. If the
		// user supplied an eval priority override, we subsequently use this.
		evalPriority := args.Job.Priority
		if args.EvalPriority > 0 {
			evalPriority = args.EvalPriority
		}

		eval = &structs.Evaluation{
			ID:          uuid.Generate(),
			Namespace:   args.RequestNamespace(),
			Priority:    evalPriority,
			Type:        args.Job.Type,
			TriggeredBy: structs.EvalTriggerJobRegister,
			JobID:       args.Job.ID,
			Status:      structs.EvalStatusPending,
			CreateTime:  now,
			ModifyTime:  now,
		}
		reply.EvalID = eval.ID
	}

	// Check if the job has changed at all
	specChanged, err := j.multiregionSpecChanged(existingJob, args)
	if err != nil {
		return err
	}

	if existingJob == nil || specChanged {

		if eval != nil {
			args.Eval = eval
			submittedEval = true
		}