Class: Pindo::TaskSystem::PindoTask

Inherits:
Object
  • Object
show all
Defined in:
lib/pindo/module/task/pindo_task.rb

Overview

PindoTask 基类(简化版,所有任务在主线程中执行)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, options = {}) ⇒ PindoTask



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
# File 'lib/pindo/module/task/pindo_task.rb', line 80

def initialize(name, options = {})
  @id = SecureRandom.uuid
  @name = name
  @type = self.class.task_type
  @task_key = self.class.task_key
  @priority = options[:priority] || TaskPriority::HIGH
  @status = TaskStatus::PENDING
  @dependencies = options[:dependencies] || []
  @context = options[:context] || {}
  @metadata = options[:metadata] || {}
  @error = nil
  @result = nil
  @created_at = Time.now
  @started_at = nil
  @finished_at = nil
  @callbacks = {
    before: [],
    after: [],
    on_success: [],
    on_failure: []
  }
  @callbacks_setup = false

  # 重试配置
  @retry_mode = options[:retry_mode] || self.class.default_retry_mode
  @retry_count = options[:retry_count] || self.class.default_retry_count
  @max_retry_count = @retry_count  # 保存初始最大重试次数
  @retry_delay = options[:retry_delay] || self.class.default_retry_delay
  @skip_count = 0  # 记录因资源不足被跳过的次数(用于解决饥饿问题)
end

Instance Attribute Details

#callbacks_setupObject

标记回调是否已经设置



43
44
45
# File 'lib/pindo/module/task/pindo_task.rb', line 43

def callbacks_setup
  @callbacks_setup
end

#contextObject

Returns the value of attribute context.



39
40
41
# File 'lib/pindo/module/task/pindo_task.rb', line 39

def context
  @context
end

#created_atObject (readonly)

Returns the value of attribute created_at.



40
41
42
# File 'lib/pindo/module/task/pindo_task.rb', line 40

def created_at
  @created_at
end

#dependenciesObject (readonly)

Returns the value of attribute dependencies.



38
39
40
# File 'lib/pindo/module/task/pindo_task.rb', line 38

def dependencies
  @dependencies
end

#errorObject

Returns the value of attribute error.



37
38
39
# File 'lib/pindo/module/task/pindo_task.rb', line 37

def error
  @error
end

#finished_atObject (readonly)

Returns the value of attribute finished_at.



40
41
42
# File 'lib/pindo/module/task/pindo_task.rb', line 40

def finished_at
  @finished_at
end

#idObject

Returns the value of attribute id.



37
38
39
# File 'lib/pindo/module/task/pindo_task.rb', line 37

def id
  @id
end

#max_retry_countObject (readonly)

max_retry_count: 初始最大重试次数



42
43
44
# File 'lib/pindo/module/task/pindo_task.rb', line 42

def max_retry_count
  @max_retry_count
end

#metadataObject

Returns the value of attribute metadata.



39
40
41
# File 'lib/pindo/module/task/pindo_task.rb', line 39

def 
  @metadata
end

#nameObject

Returns the value of attribute name.



37
38
39
# File 'lib/pindo/module/task/pindo_task.rb', line 37

def name
  @name
end

#priorityObject (readonly)

Returns the value of attribute priority.



38
39
40
# File 'lib/pindo/module/task/pindo_task.rb', line 38

def priority
  @priority
end

#resultObject

Returns the value of attribute result.



37
38
39
# File 'lib/pindo/module/task/pindo_task.rb', line 37

def result
  @result
end

#retry_countObject

剩余重试次数



41
42
43
# File 'lib/pindo/module/task/pindo_task.rb', line 41

def retry_count
  @retry_count
end

#retry_delayObject (readonly)

max_retry_count: 初始最大重试次数



42
43
44
# File 'lib/pindo/module/task/pindo_task.rb', line 42

def retry_delay
  @retry_delay
end

#retry_modeObject (readonly)

max_retry_count: 初始最大重试次数



42
43
44
# File 'lib/pindo/module/task/pindo_task.rb', line 42

def retry_mode
  @retry_mode
end

#skip_countObject

因资源不足被跳过的次数



45
46
47
# File 'lib/pindo/module/task/pindo_task.rb', line 45

def skip_count
  @skip_count
end

#started_atObject (readonly)

Returns the value of attribute started_at.



40
41
42
# File 'lib/pindo/module/task/pindo_task.rb', line 40

def started_at
  @started_at
end

#statusObject

Returns the value of attribute status.



37
38
39
# File 'lib/pindo/module/task/pindo_task.rb', line 37

def status
  @status
end

#task_keyObject (readonly)

Returns the value of attribute task_key.



38
39
40
# File 'lib/pindo/module/task/pindo_task.rb', line 38

def task_key
  @task_key
end

#task_managerObject

TaskManager 实例(依赖注入)



44
45
46
# File 'lib/pindo/module/task/pindo_task.rb', line 44

def task_manager
  @task_manager
end

#typeObject (readonly)

Returns the value of attribute type.



38
39
40
# File 'lib/pindo/module/task/pindo_task.rb', line 38

def type
  @type
end

Class Method Details

.default_retry_countObject



125
126
127
# File 'lib/pindo/module/task/pindo_task.rb', line 125

def self.default_retry_count
  0  # 默认不重试
end

.default_retry_delayObject



129
130
131
# File 'lib/pindo/module/task/pindo_task.rb', line 129

def self.default_retry_delay
  10  # 默认延迟 10 秒
end

.default_retry_modeObject

默认重试配置



121
122
123
# File 'lib/pindo/module/task/pindo_task.rb', line 121

def self.default_retry_mode
  RetryMode::IMMEDIATE
end

.task_keyObject

Raises:

  • (NotImplementedError)


116
117
118
# File 'lib/pindo/module/task/pindo_task.rb', line 116

def self.task_key
  raise NotImplementedError, "Subclass must define task_key"
end

.task_typeObject

子类必须实现的方法

Raises:

  • (NotImplementedError)


112
113
114
# File 'lib/pindo/module/task/pindo_task.rb', line 112

def self.task_type
  raise NotImplementedError, "Subclass must define task_type"
end

Instance Method Details

#before_retryObject



370
371
372
# File 'lib/pindo/module/task/pindo_task.rb', line 370

def before_retry
  # 子类可重写,进行重试前的清理工作
end

#cancelObject

取消任务



154
155
156
157
158
159
160
161
# File 'lib/pindo/module/task/pindo_task.rb', line 154

def cancel
  if @status == TaskStatus::PENDING
    @status = TaskStatus::CANCELLED
    true
  else
    false
  end
end

#cancelled?Boolean

检查是否已取消(不抛异常)



178
179
180
# File 'lib/pindo/module/task/pindo_task.rb', line 178

def cancelled?
  @status == TaskStatus::CANCELLED
end

#check_cancelled!Object

检查是否已取消,如果已取消则抛出异常



171
172
173
174
175
# File 'lib/pindo/module/task/pindo_task.rb', line 171

def check_cancelled!
  if @status == TaskStatus::CANCELLED
    raise TaskCancelledException.new("任务已被取消: #{@name}")
  end
end

#data_paramHash

获取任务的数据参数(用于传递给其他任务)



216
217
218
219
220
221
222
223
224
# File 'lib/pindo/module/task/pindo_task.rb', line 216

def data_param
  {
    task_id: @id,
    task_type: @type,
    task_key: @task_key,
    task_name: @name,
    task_param: build_task_param
  }
end

#do_taskObject

执行任务(在主线程中同步执行)



134
135
136
# File 'lib/pindo/module/task/pindo_task.rb', line 134

def do_task
  execute_internal
end

#execution_timeObject

执行时间



164
165
166
167
168
# File 'lib/pindo/module/task/pindo_task.rb', line 164

def execution_time
  return nil unless @started_at
  end_time = @finished_at || Time.now
  end_time - @started_at
end

#finished?Boolean

检查是否完成



139
140
141
# File 'lib/pindo/module/task/pindo_task.rb', line 139

def finished?
  [TaskStatus::SUCCESS, TaskStatus::FAILED, TaskStatus::CANCELLED].include?(@status)
end

#get_all_data_paramsArray<Hash>

获取所有依赖任务的数据参数



238
239
240
# File 'lib/pindo/module/task/pindo_task.rb', line 238

def get_all_data_params
  @dependencies.map { |task_id| get_data_param(task_id) }.compact
end

#get_all_data_params_by_key(task_key) ⇒ Array<Hash>

获取所有指定 task_key 的数据参数



256
257
258
# File 'lib/pindo/module/task/pindo_task.rb', line 256

def get_all_data_params_by_key(task_key)
  get_all_data_params.select { |param| param[:task_key] == task_key }
end

#get_all_dependencies_resultsHash

获取所有依赖任务的结果(按依赖顺序)



204
205
206
207
208
209
210
# File 'lib/pindo/module/task/pindo_task.rb', line 204

def get_all_dependencies_results
  results = {}
  @dependencies.each do |dep_id|
    results[dep_id] = get_dependency_result(dep_id)
  end
  results
end

#get_data_param(task_id) ⇒ Hash?

获取指定数据依赖任务的数据参数



229
230
231
232
233
234
# File 'lib/pindo/module/task/pindo_task.rb', line 229

def get_data_param(task_id)
  dep_task = get_dependency_task(task_id)
  return nil unless dep_task
  return nil unless dep_task.finished? && dep_task.status == TaskStatus::SUCCESS
  dep_task.data_param
end

#get_data_param_by_key(task_key) ⇒ Hash?

根据 task_key 获取依赖任务的数据参数



245
246
247
248
249
250
251
# File 'lib/pindo/module/task/pindo_task.rb', line 245

def get_data_param_by_key(task_key)
  @dependencies.each do |task_id|
    param = get_data_param(task_id)
    return param if param && param[:task_key] == task_key
  end
  nil
end

#get_dependency_result(dep_task_id) ⇒ Hash?

获取指定依赖任务的结果



195
196
197
198
199
200
# File 'lib/pindo/module/task/pindo_task.rb', line 195

def get_dependency_result(dep_task_id)
  dep_task = get_dependency_task(dep_task_id)
  return nil unless dep_task
  return nil unless dep_task.finished?
  dep_task.result
end

#get_dependency_task(dep_task_id) ⇒ PindoTask?

获取指定依赖任务



187
188
189
190
# File 'lib/pindo/module/task/pindo_task.rb', line 187

def get_dependency_task(dep_task_id)
  return nil unless @task_manager
  @task_manager.find_task(dep_task_id)
end

#on(event, &block) ⇒ Object

添加回调



357
358
359
# File 'lib/pindo/module/task/pindo_task.rb', line 357

def on(event, &block)
  @callbacks[event] << block if @callbacks[event]
end

#primary_data_paramHash?

获取主数据参数(第一个依赖任务的参数)



262
263
264
265
# File 'lib/pindo/module/task/pindo_task.rb', line 262

def primary_data_param
  return nil if @dependencies.empty?
  get_data_param(@dependencies.first)
end

#release_resource(resource_spec) ⇒ Object

提前释放资源释放 required_resources 中声明的资源,允许其他任务使用

Examples:

释放单个资源

def do_work
  git_pull
  release_resource({ type: :git, directory: @path })

  xcode_build  # Git 已释放,其他任务可以使用
end

释放多个资源

release_resources([
  { type: :git, directory: @path },
  { type: :xcode, directory: @path }
])

Raises:

  • (RuntimeError)

    如果任务未运行



339
340
341
342
343
344
345
346
# File 'lib/pindo/module/task/pindo_task.rb', line 339

def release_resource(resource_spec)
  raise RuntimeError, "Task not running (task_manager is nil)" unless @task_manager

  resource_manager = @task_manager.resource_lock_manager
  resource_specs = [resource_spec].flatten

  resource_manager.release_partial(resource_specs, @id)
end

#release_resources(resource_specs) ⇒ Object

批量释放资源(release_resource 的别名)



350
351
352
# File 'lib/pindo/module/task/pindo_task.rb', line 350

def release_resources(resource_specs)
  release_resource(resource_specs)
end

#required_resourcesArray<Hash>

获取任务需要的资源(子类覆盖以声明所需资源)资源规格格式:

{ type: :xcode, directory: "/path/to/project" }  # 基于目录的资源
{ type: :keychain }                              # 全局资源

Examples:

基于目录的资源(相同目录互斥)

class IOSBuildTask < PindoTask
  def initialize(name:, project_path:)
    super(name)
    @project_path = project_path
  end

  def required_resources
    [
      { type: :xcode, directory: @project_path },
      { type: :git, directory: @project_path },
      { type: :keychain }
    ]
  end
end

全局资源

class UploadTask < PindoTask
  def required_resources
    [{ type: :network }]
  end
end


76
77
78
# File 'lib/pindo/module/task/pindo_task.rb', line 76

def required_resources
  []  # 默认不需要资源
end

#reset_for_retryObject



374
375
376
377
378
379
# File 'lib/pindo/module/task/pindo_task.rb', line 374

def reset_for_retry
  @status = TaskStatus::PENDING
  @result = nil
  @started_at = nil
  @finished_at = nil
end

#retryable?Boolean

重试相关方法



362
363
364
# File 'lib/pindo/module/task/pindo_task.rb', line 362

def retryable?
  @retry_count > 0
end

#running?Boolean

是否正在运行



144
145
146
# File 'lib/pindo/module/task/pindo_task.rb', line 144

def running?
  @status == TaskStatus::RUNNING
end

#should_retry?(error) ⇒ Boolean



366
367
368
# File 'lib/pindo/module/task/pindo_task.rb', line 366

def should_retry?(error)
  true  # 默认所有错误都重试,子类可重写
end

#validateObject

验证任务是否可以执行



149
150
151
# File 'lib/pindo/module/task/pindo_task.rb', line 149

def validate
  true
end

#with_resource(resource_spec, timeout: 30) { ... } ⇒ Object

临时锁定资源(块语法,推荐使用)在代码块执行期间锁定资源,自动释放

Examples:

单个资源

with_resource({ type: :unity, directory: @path }) do
  build_unity
end

多个资源

with_resources([
  { type: :xcode, directory: @path },
  { type: :keychain }
]) do
  sign_ipa
end

Yields:

  • 持有资源时执行的代码块

Raises:

  • (ResourceLockTimeout)

    如果无法获取资源

  • (RuntimeError)

    如果任务未运行或未提供代码块



290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/pindo/module/task/pindo_task.rb', line 290

def with_resource(resource_spec, timeout: 30)
  raise ArgumentError, "Block required" unless block_given?
  raise RuntimeError, "Task not running (task_manager is nil)" unless @task_manager

  resource_manager = @task_manager.resource_lock_manager
  resource_specs = [resource_spec].flatten

  # 阻塞式获取资源
  success = resource_manager.acquire_blocking(resource_specs, @id, timeout: timeout)

  unless success
    raise ResourceLockTimeout, "无法获取资源: #{resource_specs.inspect}(超时#{timeout}秒)"
  end

  begin
    yield  # 执行代码块
  ensure
    # 自动释放临时资源
    resource_manager.release_partial(resource_specs, @id)
  end
end

#with_resources(resource_specs, timeout: 30) { ... } ⇒ Object

批量临时锁定资源(with_resource 的别名)

Yields:

  • 持有资源时执行的代码块



316
317
318
# File 'lib/pindo/module/task/pindo_task.rb', line 316

def with_resources(resource_specs, timeout: 30, &block)
  with_resource(resource_specs, timeout: timeout, &block)
end