github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/ais/prxbsumm.go (about)

     1  // Package ais provides core functionality for the AIStore object storage.
     2  /*
     3   * Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved.
     4   */
     5  package ais
     6  
     7  import (
     8  	"net/http"
     9  	"net/url"
    10  	"strconv"
    11  
    12  	"github.com/NVIDIA/aistore/api/apc"
    13  	"github.com/NVIDIA/aistore/cmn"
    14  	"github.com/NVIDIA/aistore/cmn/cos"
    15  	"github.com/NVIDIA/aistore/cmn/debug"
    16  	"github.com/NVIDIA/aistore/core/meta"
    17  )
    18  
    19  // in this source:
    20  // - bsummact  <= api.GetBucketSummary(query-bcks, ActMsg)
    21  // - bsummhead <= api.GetBucketInfo(bck, QparamBinfoWithOrWithoutRemote)
    22  
    23  func (p *proxy) bsummact(w http.ResponseWriter, r *http.Request, qbck *cmn.QueryBcks, msg *apc.BsummCtrlMsg) {
    24  	news := msg.UUID == ""
    25  	debug.Assert(msg.UUID == "" || cos.IsValidUUID(msg.UUID), msg.UUID)
    26  
    27  	// start new
    28  	if news {
    29  		err := p.bsummNew(qbck, msg)
    30  		if err != nil {
    31  			p.writeErr(w, r, err)
    32  		} else {
    33  			w.WriteHeader(http.StatusAccepted)
    34  			w.Header().Set(cos.HdrContentLength, strconv.Itoa(len(msg.UUID)))
    35  			w.Write([]byte(msg.UUID))
    36  		}
    37  		return
    38  	}
    39  
    40  	// or, query partial or final results
    41  	summaries, status, err := p.bsummCollect(qbck, msg)
    42  	if err != nil {
    43  		p.writeErr(w, r, err)
    44  		return
    45  	}
    46  	w.WriteHeader(status)
    47  	p.writeJSON(w, r, summaries, "bucket-summary")
    48  }
    49  
    50  func (p *proxy) bsummNew(qbck *cmn.QueryBcks, msg *apc.BsummCtrlMsg) (err error) {
    51  	q := qbck.NewQuery()
    52  
    53  	msg.UUID = cos.GenUUID()
    54  	aisMsg := p.newAmsgActVal(apc.ActSummaryBck, msg)
    55  
    56  	args := allocBcArgs()
    57  	args.req = cmn.HreqArgs{
    58  		Method: http.MethodGet,
    59  		Path:   apc.URLPathBuckets.Join(qbck.Name, apc.ActBegin), // compare w/ txn
    60  		Query:  q,
    61  		Body:   cos.MustMarshal(aisMsg),
    62  	}
    63  	args.smap = p.owner.smap.get()
    64  	if cnt := args.smap.CountActiveTs(); cnt < 1 {
    65  		return cmn.NewErrNoNodes(apc.Target, args.smap.CountTargets())
    66  	}
    67  	results := p.bcastGroup(args)
    68  	for _, res := range results {
    69  		if res.err != nil {
    70  			err = res.toErr()
    71  			break
    72  		}
    73  	}
    74  	freeBcastRes(results)
    75  	return
    76  }
    77  
    78  func (p *proxy) bsummCollect(qbck *cmn.QueryBcks, msg *apc.BsummCtrlMsg) (_ cmn.AllBsummResults, status int, _ error) {
    79  	var (
    80  		q      = make(url.Values, 4)
    81  		aisMsg = p.newAmsgActVal(apc.ActSummaryBck, msg)
    82  		args   = allocBcArgs()
    83  	)
    84  	args.req = cmn.HreqArgs{
    85  		Method: http.MethodGet,
    86  		Path:   apc.URLPathBuckets.Join(qbck.Name, apc.ActQuery),
    87  		Body:   cos.MustMarshal(aisMsg),
    88  	}
    89  	args.smap = p.owner.smap.get()
    90  	if cnt := args.smap.CountActiveTs(); cnt < 1 {
    91  		return nil, 0, cmn.NewErrNoNodes(apc.Target, args.smap.CountTargets())
    92  	}
    93  	qbck.AddToQuery(q)
    94  	q.Set(apc.QparamSilent, "true")
    95  	args.req.Query = q
    96  	args.cresv = cresBsumm{} // -> cmn.AllBsummResults
    97  
    98  	results := p.bcastGroup(args)
    99  	freeBcArgs(args)
   100  	for _, res := range results {
   101  		if res.err != nil {
   102  			freeBcastRes(results)
   103  			return nil, 0, res.toErr()
   104  		}
   105  	}
   106  
   107  	var (
   108  		summaries   = make(cmn.AllBsummResults, 0, 8)
   109  		dsize       = make(map[string]uint64, len(results))
   110  		numAccepted int
   111  		numPartial  int
   112  	)
   113  	for _, res := range results {
   114  		if res.status == http.StatusAccepted {
   115  			numAccepted++
   116  			continue
   117  		}
   118  		if res.status == http.StatusPartialContent {
   119  			numPartial++
   120  		}
   121  		tbsumm, tid := res.v.(*cmn.AllBsummResults), res.si.ID()
   122  		for _, summ := range *tbsumm {
   123  			dsize[tid] = summ.TotalSize.Disks
   124  			summaries = summaries.Aggregate(summ)
   125  		}
   126  	}
   127  	summaries.Finalize(dsize, cmn.Rom.TestingEnv())
   128  	freeBcastRes(results)
   129  
   130  	switch {
   131  	case numPartial == 0 && numAccepted == 0:
   132  		status = http.StatusOK
   133  	case numPartial == 0:
   134  		status = http.StatusAccepted
   135  	default:
   136  		status = http.StatusPartialContent
   137  	}
   138  	return summaries, status, nil
   139  }
   140  
   141  // fully reuse bsummact impl.
   142  func (p *proxy) bsummhead(bck *meta.Bck, msg *apc.BsummCtrlMsg) (info *cmn.BsummResult, status int, err error) {
   143  	var (
   144  		summaries cmn.AllBsummResults
   145  		qbck      = (*cmn.QueryBcks)(bck) // adapt
   146  	)
   147  	if msg.UUID == "" {
   148  		if err = p.bsummNew(qbck, msg); err == nil {
   149  			status = http.StatusAccepted
   150  		}
   151  		return
   152  	}
   153  	summaries, status, err = p.bsummCollect(qbck, msg)
   154  	if err == nil && (status == http.StatusOK || status == http.StatusPartialContent) {
   155  		info = summaries[0]
   156  	}
   157  	return
   158  }