Skip to content

Commit d2d0299

Browse files
zhaoxianhuagithubgxll
authored andcommitted
[fix][dingospeed] Added xet adaptation for large file downloads
1 parent 222ec3d commit d2d0299

23 files changed

Lines changed: 396 additions & 275 deletions

cmd/wire_gen.go

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docker/Dockerfile-alpine

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ ENV TZ=Asia/Shanghai
1919
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
2020

2121
# 安装 huggingface-hub(指定版本)
22-
RUN pip3 install --no-cache-dir --break-system-packages huggingface-hub==0.36.0
22+
RUN pip3 install --no-cache-dir --break-system-packages huggingface-hub==1.2.3
2323
RUN pip3 install --no-cache-dir --break-system-packages hf_transfer==0.1.9
24+
RUN pip3 install --no-cache-dir --break-system-packages hf-xet==1.2.0
2425
# 验证安装
25-
RUN huggingface-cli version
26+
RUN hf version
2627
CMD ["/bin/sh"]
2728

28-
# docker build -t huggingface-hub:alpine-0.34.4 -f Dockerfile-alpine .
29+
# docker build -t huggingface-hub:alpine-1.2.3 -f Dockerfile-alpine .

docker/Dockerfile-alpine-simple

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM huggingface-hub:alpine-0.34.4
1+
FROM huggingface-hub:alpine-1.2.3
22

33
WORKDIR /app
44

internal/dao/file_dao.go

Lines changed: 127 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ func (f *FileDao) FileGetGenerator(c echo.Context, repoType, orgRepo, commit, fi
173173
}
174174
authorization := c.Request().Header.Get("Authorization")
175175
// _file_realtime_stream
176-
pathsInfos, err := f.GetPathsInfo(repoType, orgRepo, commit, authorization, []string{fileName})
176+
pathInfo, err := f.GetPathsInfo(hfUri, repoType, orgRepo, commit, authorization, fileName)
177177
if err != nil {
178178
if e, ok := err.(myerr.Error); ok {
179179
zap.S().Errorf("GetPathsInfo code:%d, err:%v", e.StatusCode(), err)
@@ -182,42 +182,15 @@ func (f *FileDao) FileGetGenerator(c echo.Context, repoType, orgRepo, commit, fi
182182
zap.S().Errorf("GetPathsInfo err:%v", err)
183183
return util.ErrorProxyError(c)
184184
}
185-
if len(pathsInfos) == 0 {
185+
if pathInfo == nil {
186186
zap.S().Errorf("pathsInfos is null. repo:%s, commit:%s, fileName:%s", orgRepo, commit, fileName)
187187
return util.ErrorEntryNotFound(c)
188188
}
189-
if len(pathsInfos) != 1 {
190-
zap.S().Errorf("pathsInfos not equal to 1. repo:%s, commit:%s, fileName:%s", orgRepo, commit, fileName)
191-
return util.ErrorProxyTimeout(c)
192-
}
193-
pathInfo := pathsInfos[0]
194189
if pathInfo.Type == "directory" {
195190
zap.S().Warnf("repo:%s, commit:%s, fileName:%s is directory", orgRepo, commit, fileName)
196191
return util.ErrorEntryNotFound(c)
197192
}
198-
var startPos, endPos int64
199-
if pathInfo.Size > 0 { // There exists a file of size 0
200-
var headRange = c.Request().Header.Get("Range")
201-
if headRange == "" {
202-
headRange = fmt.Sprintf("bytes=%d-%d", 0, pathInfo.Size-1)
203-
}
204-
startPos, endPos = parseRangeParams(headRange, pathInfo.Size)
205-
endPos = endPos + 1
206-
} else if pathInfo.Size == 0 {
207-
zap.S().Warnf("file %s size: %d", fileName, pathInfo.Size)
208-
}
209-
respHeaders := map[string]string{}
210-
respHeaders["content-length"] = util.Itoa(endPos - startPos)
211-
if commit != "" {
212-
respHeaders[strings.ToLower(consts.HUGGINGFACE_HEADER_X_REPO_COMMIT)] = commit
213-
}
214-
var etag string
215-
if pathInfo.Lfs.Oid != "" {
216-
etag = pathInfo.Lfs.Oid
217-
} else {
218-
etag = pathInfo.Oid
219-
}
220-
respHeaders["etag"] = etag
193+
respHeaders, etag, startPos, endPos := constructRespHeader(c, pathInfo, commit, fileName)
221194
blobsDir := fmt.Sprintf("%s/files/%s/%s/blobs", config.SysConfig.Repos(), repoType, orgRepo)
222195
blobsFile := fmt.Sprintf("%s/%s", blobsDir, etag)
223196
filesDir := fmt.Sprintf("%s/files/%s/%s/resolve/%s", config.SysConfig.Repos(), repoType, orgRepo, commit)
@@ -245,6 +218,46 @@ func (f *FileDao) FileGetGenerator(c echo.Context, repoType, orgRepo, commit, fi
245218
}
246219
}
247220

221+
func constructRespHeader(c echo.Context, pathInfo *common.PathsInfo, commit, fileName string) (map[string]string, string, int64, int64) {
222+
var startPos, endPos int64
223+
if pathInfo.Size > 0 { // There exists a file of size 0
224+
var headRange = c.Request().Header.Get("Range")
225+
if headRange == "" {
226+
headRange = fmt.Sprintf("bytes=%d-%d", 0, pathInfo.Size-1)
227+
}
228+
startPos, endPos = parseRangeParams(headRange, pathInfo.Size)
229+
endPos = endPos + 1
230+
} else if pathInfo.Size == 0 {
231+
zap.S().Warnf("file %s size: %d", fileName, pathInfo.Size)
232+
}
233+
respHeaders := map[string]string{}
234+
respHeaders[consts.HUGGINGFACE_HEADER_CONTENT_LENGTH] = util.Itoa(endPos - startPos)
235+
if commit != "" {
236+
respHeaders[strings.ToLower(consts.HUGGINGFACE_HEADER_X_REPO_COMMIT)] = commit
237+
}
238+
var etag string
239+
if pathInfo.Lfs.Oid != "" {
240+
etag = pathInfo.Lfs.Oid
241+
} else {
242+
etag = pathInfo.Oid
243+
}
244+
respHeaders[consts.HUGGINGFACE_HEADER_X_LINKED_ETAG] = etag
245+
respHeaders[consts.HUGGINGFACE_HEADER_X_LINKED_SIZE] = util.Itoa(pathInfo.Size)
246+
if pathInfo.Location != "" {
247+
// clientHost := c.Request().Host
248+
// clientScheme := "http"
249+
// if c.Request().TLS != nil {
250+
// clientScheme = "https"
251+
// }
252+
// hfEndPoint := fmt.Sprintf("%s://%s", clientScheme, clientHost)
253+
// loc := strings.ReplaceAll(pathInfo.Location, config.SysConfig.GetXetURLBase(), hfEndPoint)
254+
respHeaders[consts.HUGGINGFACE_LOCATION] = pathInfo.Location
255+
respHeaders[consts.HUGGINGFACE_HEADER_X_XET_HASH] = pathInfo.XXetHash
256+
respHeaders[consts.HUGGINGFACE_Link] = pathInfo.Link
257+
}
258+
return respHeaders, etag, startPos, endPos
259+
}
260+
248261
func GetAnalysisFilePosition(dingFile *downloader.DingCache, startPos, endPos int64) int64 {
249262
_, offset := analysisFilePosition(dingFile, startPos, endPos)
250263
return offset
@@ -293,88 +306,90 @@ func (f *FileDao) ConstructBlobsAndFileFile(blobsFile, filesPath string) (err er
293306
return
294307
}
295308

296-
func (f *FileDao) GetPathsInfo(repoType, orgRepo, commit, authorization string, pathFileNames []string) ([]common.PathsInfo, error) {
297-
ret := make([]common.PathsInfo, 0)
298-
remoteReqFilePathMap := make(map[string]string)
299-
if len(pathFileNames) == 0 {
300-
return ret, nil
309+
func (f *FileDao) GetPathsInfo(hfUri, repoType, orgRepo, commit, authorization string, pathFileName string) (*common.PathsInfo, error) {
310+
var pathInfo *common.PathsInfo
311+
if pathFileName == "" {
312+
return nil, fmt.Errorf("pathFileName is null, %s/%s", orgRepo, commit)
301313
}
314+
apiPathInfoPath := fmt.Sprintf("%s/api/%s/%s/paths-info/%s/%s/paths-info_post.json", config.SysConfig.Repos(), repoType, orgRepo, commit, pathFileName)
302315
// 对每个用户检测是否有权限,在线、离线都检测,都需要携带token。
303316
filePathInfoKey := GetFilePathInfoKey(repoType, orgRepo, authorization)
304317
_, granted := f.baseData.Cache.Get(filePathInfoKey)
305-
for _, pathFileName := range pathFileNames {
306-
apiPathInfoPath := fmt.Sprintf("%s/api/%s/%s/paths-info/%s/%s/paths-info_post.json", config.SysConfig.Repos(), repoType, orgRepo, commit, pathFileName)
307-
if !granted {
308-
remoteReqFilePathMap[pathFileName] = apiPathInfoPath
309-
continue
310-
}
311-
hitCache := util.FileExists(apiPathInfoPath)
312-
if hitCache {
313-
if cacheContent, err := f.ReadCacheRequest(apiPathInfoPath); err != nil { // 若存在缓存文件,却读取失败,将会在线请求。
314-
zap.S().Errorf("ReadCacheRequest err.%v", err)
315-
} else {
316-
if cacheContent.Version != consts.VersionSnapshot {
317-
// 若是老版本,需要重新请求pathsInfo数据
318+
if granted && f.ExistApiPathFile(apiPathInfoPath) { // 已授权的用户,才可以从缓存读取,若存在则返回,否则一律在线请求。
319+
if cacheContent, err := f.ReadCacheRequest(apiPathInfoPath); err != nil { // 若存在缓存文件,却读取失败,将会在线请求。
320+
zap.S().Warnf("ReadCacheRequest err, go to online access%v", err)
321+
} else {
322+
if cacheContent.Version == consts.VersionSnapshot {
323+
pathsInfos := make([]common.PathsInfo, 0)
324+
if err = sonic.Unmarshal(cacheContent.OriginContent, &pathsInfos); err != nil {
325+
zap.S().Errorf("pathsInfo Unmarshal err.%v", err)
318326
} else {
319-
pathsInfos := make([]common.PathsInfo, 0)
320-
if err = sonic.Unmarshal(cacheContent.OriginContent, &pathsInfos); err != nil {
321-
zap.S().Errorf("pathsInfo Unmarshal err.%v", err)
322-
} else if cacheContent.StatusCode == http.StatusOK {
323-
ret = append(ret, pathsInfos...)
324-
continue
327+
if len(pathsInfos) > 0 {
328+
if pathsInfos[0].Size > consts.MAX_HTTP_DOWNLOAD_SIZE {
329+
goto requestRemoteFileInfo
330+
} else {
331+
return &pathsInfos[0], nil
332+
}
325333
}
326334
}
327335
}
328336
}
329-
// 若命中缓存读取失败,将执行在线
330-
remoteReqFilePathMap[pathFileName] = apiPathInfoPath
331337
}
332-
if len(remoteReqFilePathMap) > 0 {
333-
filePaths := make([]string, 0)
334-
for k := range remoteReqFilePathMap {
335-
filePaths = append(filePaths, k)
336-
}
337-
response, err := f.requestFilePath(repoType, orgRepo, commit, authorization, filePaths)
338-
if err != nil {
339-
return nil, err
340-
}
338+
goto requestRemoteFileInfo
339+
340+
requestRemoteFileInfo:
341+
pathsInfoUri := fmt.Sprintf("/api/%s/%s/paths-info/%s", repoType, orgRepo, commit)
342+
if response, err := f.requestFilePathInfo(pathsInfoUri, authorization, []string{pathFileName}); err != nil {
343+
return nil, err
344+
} else {
341345
if !granted {
342346
f.baseData.Cache.Set(filePathInfoKey, "", 24*time.Hour)
343347
}
344-
remoteRespPathsInfos := make([]common.PathsInfo, 0)
348+
remoteRespPathsInfos := make([]*common.PathsInfo, 0)
345349
err = sonic.Unmarshal(response.Body, &remoteRespPathsInfos)
346350
if err != nil {
347351
return nil, myerr.NewAppendCode(http.StatusInternalServerError, fmt.Sprintf("%v", err))
348352
}
349-
for _, item := range remoteRespPathsInfos {
350-
// 对单个文件pathsInfo做存储
351-
if apiPath, ok := remoteReqFilePathMap[item.Path]; ok {
352-
if err = util.MakeDirs(apiPath); err != nil {
353-
zap.S().Errorf("create %s dir err.%v", apiPath, err)
354-
continue
355-
}
356-
if item.Type == "file" {
357-
b, _ := sonic.Marshal([]common.PathsInfo{item}) // 转成单个文件的切片
358-
if err = f.WriteCacheRequest(apiPath, response.StatusCode, response.ExtractHeaders(response.Headers), b); err != nil {
359-
zap.S().Errorf("WriteCacheRequest err.%s,%v", apiPath, err)
360-
continue
361-
}
362-
}
353+
if len(remoteRespPathsInfos) > 0 {
354+
pathInfo = remoteRespPathsInfos[0]
355+
} else {
356+
return nil, myerr.NewAppendCode(http.StatusNotFound, "remoteRespPathsInfos is null")
357+
}
358+
if pathInfo.Size > consts.MAX_HTTP_DOWNLOAD_SIZE {
359+
if resolveResp, err := f.requestFileResolve(hfUri, authorization); err != nil {
360+
return nil, err
361+
} else {
362+
pathInfo.XXetHash = resolveResp.GetKey(consts.HUGGINGFACE_HEADER_X_XET_HASH)
363+
pathInfo.Location = resolveResp.GetKey(consts.HUGGINGFACE_LOCATION)
364+
pathInfo.Link = resolveResp.GetKey(consts.HUGGINGFACE_Link)
363365
}
364366
}
365-
ret = append(ret, remoteRespPathsInfos...)
367+
ret := []*common.PathsInfo{pathInfo}
368+
b, _ := sonic.Marshal(ret) // 转成单个文件的切片
369+
if err = util.MakeDirs(apiPathInfoPath); err != nil {
370+
return nil, fmt.Errorf("create %s dir err.%v", apiPathInfoPath, err)
371+
}
372+
if err = f.WriteCacheRequest(apiPathInfoPath, response.StatusCode, response.ExtractHeaders(response.Headers), b); err != nil {
373+
return nil, fmt.Errorf("WriteCacheRequest err.%s,%v", apiPathInfoPath, err)
374+
}
366375
}
367-
return ret, nil
376+
return pathInfo, nil
368377
}
369378

370-
func (f *FileDao) requestFilePath(repoType, orgRepo, commit, authorization string, filePaths []string) (*common.Response, error) {
371-
pathsInfoUri := fmt.Sprintf("/api/%s/%s/paths-info/%s", repoType, orgRepo, commit)
372-
response, err := f.pathsInfoProxy(pathsInfoUri, authorization, filePaths)
379+
func (f *FileDao) requestFileResolve(fileResolveUri, authorization string) (*common.Response, error) {
380+
headers := map[string]string{}
381+
if authorization != "" {
382+
headers["authorization"] = authorization
383+
}
384+
response, err := util.RetryRequest(func() (*common.Response, error) {
385+
return util.Head(fileResolveUri, headers)
386+
})
373387
if err != nil {
374-
zap.S().Errorf("req %s err.%v", pathsInfoUri, err)
388+
zap.S().Errorf("req %s err.%v", fileResolveUri, err)
375389
return nil, myerr.NewAppendCode(http.StatusInternalServerError, fmt.Sprintf("%v", err))
376390
}
377-
if response.StatusCode != http.StatusOK {
391+
// 非成功或重定向
392+
if response.StatusCode != http.StatusOK && !(response.StatusCode >= http.StatusMultipleChoices && response.StatusCode <= http.StatusPermanentRedirect) {
378393
var errorResp common.ErrorResp
379394
if len(response.Body) > 0 {
380395
err = sonic.Unmarshal(response.Body, &errorResp)
@@ -387,7 +402,7 @@ func (f *FileDao) requestFilePath(repoType, orgRepo, commit, authorization strin
387402
return response, nil
388403
}
389404

390-
func (f *FileDao) pathsInfoProxy(targetUri, authorization string, filePaths []string) (*common.Response, error) {
405+
func (f *FileDao) requestFilePathInfo(pathsInfoUri, authorization string, filePaths []string) (*common.Response, error) {
391406
reqData := map[string]interface{}{
392407
"paths": filePaths,
393408
}
@@ -399,9 +414,23 @@ func (f *FileDao) pathsInfoProxy(targetUri, authorization string, filePaths []st
399414
if authorization != "" {
400415
headers["authorization"] = authorization
401416
}
402-
return util.RetryRequest(func() (*common.Response, error) {
403-
return util.Post(targetUri, "application/json", jsonData, headers)
404-
})
417+
if response, err := util.RetryRequest(func() (*common.Response, error) {
418+
return util.Post(pathsInfoUri, "application/json", jsonData, headers)
419+
}); err != nil {
420+
zap.S().Errorf("req %s err.%v", pathsInfoUri, err)
421+
return nil, myerr.NewAppendCode(http.StatusInternalServerError, fmt.Sprintf("%v", err))
422+
} else if response.StatusCode != http.StatusOK {
423+
var errorResp common.ErrorResp
424+
if len(response.Body) > 0 {
425+
err = sonic.Unmarshal(response.Body, &errorResp)
426+
if err != nil {
427+
return nil, myerr.NewAppendCode(response.StatusCode, fmt.Sprintf("response code %d, %v", response.StatusCode, err))
428+
}
429+
}
430+
return nil, myerr.NewAppendCode(response.StatusCode, errorResp.Error)
431+
} else {
432+
return response, nil
433+
}
405434
}
406435

407436
func (f *FileDao) FileChunkGet(c echo.Context, taskParam *downloader.TaskParam, startPos, endPos int64, respHeaders map[string]string) error {
@@ -440,6 +469,13 @@ func (f *FileDao) WriteCacheRequest(apiPath string, statusCode int, headers map[
440469
return util.WriteDataToFile(apiPath, cacheContent)
441470
}
442471

472+
func (f *FileDao) ExistApiPathFile(apiPath string) bool {
473+
lock := f.lockDao.getMetaFileLock(apiPath)
474+
lock.RLock()
475+
defer lock.RUnlock()
476+
return util.FileExists(apiPath)
477+
}
478+
443479
func (f *FileDao) ReadCacheRequest(apiPath string) (*common.CacheContent, error) {
444480
lock := f.lockDao.getMetaFileLock(apiPath)
445481
lock.RLock()

0 commit comments

Comments
 (0)