Skip to content

Commit 8ee7052

Browse files
eric-gechengclaude
andauthored
[feat] delta embedding dump upload to PAI Feature Store (alibaba#603)
Co-authored-by: Claude <noreply@anthropic.com>
1 parent afd97b0 commit 8ee7052

15 files changed

Lines changed: 3473 additions & 39 deletions

requirements/runtime.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ anytree
33
common_io @ https://tzrec.oss-accelerate.aliyuncs.com/third_party/common_io-0.4.1%2Btunnel-py2.py3-none-any.whl
44
confluent-kafka
55
fbgemm-gpu==1.7.0
6+
feature_store_py @ https://feature-store-py.oss-cn-beijing.aliyuncs.com/package/feature_store_py-2.2.7-py3-none-any.whl
67
fsspec
78
graphlearn @ https://tzrec.oss-accelerate.aliyuncs.com/third_party/graphlearn/graphlearn-1.3.8-cp312-cp312-linux_x86_64.whl ; python_version=="3.12"
89
graphlearn @ https://tzrec.oss-accelerate.aliyuncs.com/third_party/graphlearn/graphlearn-1.3.8-cp311-cp311-linux_x86_64.whl ; python_version=="3.11"

tzrec/main.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -606,11 +606,9 @@ def run_eval(step: int, epoch: int) -> None:
606606
_model.on_train_end()
607607
if delta_embedding_dumper is not None:
608608
# Flush the trailing partial interval before the final checkpoint.
609-
# final_dump skips dump-boundary steps already written by maybe_dump,
610-
# so it never overwrites their shards with an empty file. Ranks can
611-
# reach here at different i_step (independent dataloader exhaustion with
612-
# check_all_workers_data_status=False), so final_dump all-reduces the
613-
# step across ranks to keep one complete shard set per step dir.
609+
# final_dump skips dump-boundary steps already written by maybe_dump
610+
# (all ranks run the same step count, so every rank participated in
611+
# those dumps and reaches the same final step).
614612
delta_embedding_dumper.final_dump(i_step)
615613

616614
_log_train(
@@ -900,6 +898,8 @@ def train_and_evaluate(
900898
with open(os.path.join(pipeline_config.model_dir, "version"), "w") as f:
901899
f.write(tzrec_version + "\n")
902900

901+
if delta_embedding_dumper is not None:
902+
delta_embedding_dumper.start()
903903
# when slice batch by sample cost, data on all workers may not be balanced
904904
check_all_workers_data_status = data_config.HasField("batch_cost_size")
905905
_train_and_evaluate(
@@ -922,6 +922,12 @@ def train_and_evaluate(
922922
dense_ema=dense_ema,
923923
export_config=pipeline_config.export_config,
924924
)
925+
# Drain background uploads only after training succeeds. A training failure
926+
# terminates the whole job (torchrun tears down every rank) and pending
927+
# in-memory deltas are intentionally abandoned: the restarted run re-dumps
928+
# from the latest checkpoint, so there is nothing to roll back or undo.
929+
if delta_embedding_dumper is not None:
930+
delta_embedding_dumper.close()
925931
if is_local_rank_zero:
926932
logger.info("Train and Evaluate Finished.")
927933

tzrec/protos/train.proto

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,71 @@ message GradClipping {
2929
optional bool enable_global_grad_clip = 4 [default = false];
3030
}
3131

32+
message FeatureStoreConfig {
33+
// Cloud credentials (AK/SK/STS) are resolved at runtime through the
34+
// Alibaba Cloud default credential provider chain (alibabacloud_credentials).
35+
// FeatureDB credentials are read from FEATUREDB_USERNAME/FEATUREDB_PASSWORD.
36+
// FeatureStore control-plane region. An explicitly empty value falls back
37+
// to ALIBABA_CLOUD_REGION at runtime.
38+
required string region = 1;
39+
// Existing FeatureStore project name and target DynamicEmbedding FeatureView
40+
// name. The view is validated at startup and created when it does not exist,
41+
// together with a default FeatureStore entity that is auto-created on demand.
42+
required string project_name = 2;
43+
required string feature_view_name = 3;
44+
// FeatureDB version for this incremental training run.
45+
required string version = 5;
46+
47+
48+
// Optional FeatureStore control-plane endpoint override, passed directly to
49+
// the FeatureStore SDK, which owns endpoint handling and connection errors.
50+
optional string endpoint = 6;
51+
// Maximum records submitted before each SDK write_flush() completion gate.
52+
optional uint32 upload_batch_size = 7 [default = 1000];
53+
// Total attempts per rank for one step. All retries reuse the version;
54+
// each attempt reserves a newer monotonic ts range and fully replays the
55+
// rank's delta so incremental readers cannot miss a partial earlier attempt.
56+
optional uint32 max_retries = 8 [default = 3];
57+
optional uint32 retry_backoff_secs = 9 [default = 5];
58+
// Maximum time normal training shutdown waits for the uploader to drain.
59+
optional uint32 shutdown_timeout_secs = 10 [default = 600];
60+
// Apply back-pressure when too many completed dumps await upload; bounds
61+
// the per-rank memory retained by pending in-memory deltas.
62+
optional uint32 max_pending_steps = 11 [default = 32];
63+
optional uint32 poll_interval_secs = 12 [default = 1];
64+
// Creation settings used only when feature_view_name does not yet exist.
65+
optional uint32 feature_view_ttl_secs = 13 [default = 1296000];
66+
optional uint32 feature_view_shard_count = 14 [default = 20];
67+
optional uint32 feature_view_replication_count = 15 [default = 1];
68+
reserved 4, 16;
69+
reserved "feature_entity_name", "allow_custom_endpoint";
70+
// Also write the local per-rank delta parquet files while uploading to
71+
// FeatureStore (e.g. for the offline readback checker); by default the
72+
// delta is handed to the uploader in memory and never touches disk.
73+
optional bool retain_local_dump = 17 [default = false];
74+
// Wire format for delta upload. "ARROW" (default) streams a columnar Arrow
75+
// IPC batch through write_features_arrow(), avoiding the JSON path's per-row
76+
// dict construction and embedding deep-copy; "JSON" keeps the legacy
77+
// write_features() per-row payload. Both paths use MERGE write_mode.
78+
optional string upload_format = 18 [default = "ARROW"];
79+
}
80+
3281
message DeltaEmbeddingDumpConfig {
3382
// MC/ZCH features are not supported; use dynamicemb for delta dump.
34-
// dump touched ids and their latest embedding every N training steps. Larger
35-
// intervals retain a longer id window in memory; auto compaction reduces
83+
// Dump touched ids and their latest embedding every N training steps. Do not
84+
// set this together with dump_interval_minutes.
85+
// Larger intervals retain a longer id window in memory; auto compaction reduces
3686
// per-batch tensor buildup but unique ids still scale with the interval.
3787
optional uint32 dump_interval_steps = 1 [default = 1000];
3888
// output directory. default is ${model_dir}/delta_embedding_dump
3989
optional string output_dir = 2;
4090
// parquet file prefix
4191
optional string file_prefix = 3 [default = "delta_embedding"];
92+
// Presence enables best-effort per-rank background upload to FeatureStore.
93+
optional FeatureStoreConfig feature_store_config = 4;
94+
// Dump after this many elapsed minutes. The timer starts when training starts.
95+
// Do not set this together with dump_interval_steps.
96+
optional uint32 dump_interval_minutes = 5;
4297
}
4398

4499
message TrainConfig {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2026, Alibaba Group;
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
# Unless required by applicable law or agreed to in writing, software
7+
# distributed under the License is distributed on an "AS IS" BASIS,
8+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9+
# See the License for the specific language governing permissions and
10+
# limitations under the License.

0 commit comments

Comments
 (0)