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

Returns a new instance of PindoTask.



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

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

标记回调是否已经设置



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

def callbacks_setup
  @callbacks_setup
end

#contextObject

Returns the value of attribute context.



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

def context
  @context
end

#created_atObject (readonly)

Returns the value of attribute created_at.



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

def created_at
  @created_at
end

#dependenciesObject (readonly)

Returns the value of attribute dependencies.



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

def dependencies
  @dependencies
end

#errorObject

Returns the value of attribute error.



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

def error
  @error
end

#finished_atObject (readonly)

Returns the value of attribute finished_at.



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

def finished_at
  @finished_at
end

#idObject

Returns the value of attribute id.



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

def id
  @id
end

#max_retry_countObject (readonly)

max_retry_count: 初始最大重试次数



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

def max_retry_count
  @max_retry_count
end

#metadataObject

Returns the value of attribute metadata.



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

def 
  @metadata
end

#nameObject

Returns the value of attribute name.



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

def name
  @name
end

#priorityObject (readonly)

Returns the value of attribute priority.



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

def priority
  @priority
end

#resultObject

Returns the value of attribute result.



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

def result
  @result
end

#retry_countObject

剩余重试次数



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

def retry_count
  @retry_count
end

#retry_delayObject (readonly)

max_retry_count: 初始最大重试次数



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

def retry_delay
  @retry_delay
end

#retry_modeObject (readonly)

max_retry_count: 初始最大重试次数



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

def retry_mode
  @retry_mode
end

#skip_countObject

因资源不足被跳过的次数



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

def skip_count
  @skip_count
end

#started_atObject (readonly)

Returns the value of attribute started_at.



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

def started_at
  @started_at
end

#statusObject

Returns the value of attribute status.



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

def status
  @status
end

#task_keyObject (readonly)

Returns the value of attribute task_key.



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

def task_key
  @task_key
end

#task_managerObject

TaskManager 实例(依赖注入)



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

def task_manager
  @task_manager
end

#typeObject (readonly)

Returns the value of attribute type.



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

def type
  @type
end

Class Method Details

.default_retry_countObject



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

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

.default_retry_delayObject



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

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

.default_retry_modeObject

默认重试配置



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

def self.default_retry_mode
  RetryMode::IMMEDIATE
end

.task_keyObject

Raises:

  • (NotImplementedError)


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

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

.task_typeObject

子类必须实现的方法

Raises:

  • (NotImplementedError)


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

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

Instance Method Details

#before_retryObject



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

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

#cancelObject

取消任务



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

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

#cancelled?Boolean

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

Returns:

  • (Boolean)


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

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

#check_cancelled!Object

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



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

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

#data_paramHash

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

Returns:

  • (Hash)

    包含任务标识和参数的哈希



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

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

#do_taskObject

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



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

def do_task
  execute_internal
end

#execution_timeObject

执行时间



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

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

#finished?Boolean

检查是否完成

Returns:

  • (Boolean)


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

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

#get_all_data_paramsArray<Hash>

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

Returns:

  • (Array<Hash>)

    数据参数数组



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

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 的数据参数

Parameters:

  • task_key (Symbol)

    任务键

Returns:

  • (Array<Hash>)

    匹配的任务数据参数数组



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

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

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

Returns:

  • (Hash)

    key 为任务 ID,value 为任务结果



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

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?

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

Parameters:

  • task_id (String)

    任务 ID

Returns:

  • (Hash, nil)

    任务的数据参数,如果任务不存在或未完成返回 nil



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

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 获取依赖任务的数据参数

Parameters:

  • task_key (Symbol)

    任务键

Returns:

  • (Hash, nil)

    第一个匹配的任务数据参数



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

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?

获取指定依赖任务的结果

Parameters:

  • dep_task_id (String)

    依赖任务的 ID

Returns:

  • (Hash, nil)

    依赖任务的 result,如果任务不存在或未完成返回 nil



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

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?

获取指定依赖任务

Parameters:

  • dep_task_id (String)

    依赖任务的 ID

Returns:

  • (PindoTask, nil)

    依赖任务对象,如果不存在返回 nil



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

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

#on(event, &block) ⇒ Object

添加回调



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

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

#primary_data_paramHash?

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

Returns:

  • (Hash, nil)

    主数据参数



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

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 }
])

Parameters:

  • resource_spec (Hash, Array<Hash>)

    要释放的资源规格

Raises:

  • (RuntimeError)

    如果任务未运行



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

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 的别名)

Parameters:

  • resource_specs (Array<Hash>)

    资源规格数组



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

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

Returns:

  • (Array<Hash>)

    资源规格数组



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

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

#reset_for_retryObject



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

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

#retryable?Boolean

重试相关方法

Returns:

  • (Boolean)


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

def retryable?
  @retry_count > 0
end

#running?Boolean

是否正在运行

Returns:

  • (Boolean)


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

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

#should_retry?(error) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#validateObject

验证任务是否可以执行



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

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

Parameters:

  • resource_spec (Hash, Array<Hash>)

    资源规格

  • timeout (Integer) (defaults to: 30)

    超时时间(秒)

Yields:

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

Raises:

  • (ResourceLockTimeout)

    如果无法获取资源

  • (RuntimeError)

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



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

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 的别名)

Parameters:

  • resource_specs (Array<Hash>)

    资源规格数组

  • timeout (Integer) (defaults to: 30)

    超时时间(秒)

Yields:

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



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

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