Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Floors: Dynamic fetch #2732

Merged
merged 27 commits into from
Nov 16, 2023
Merged

Conversation

pm-nikhil-vaidya
Copy link
Contributor

@pm-nikhil-vaidya pm-nikhil-vaidya commented Apr 26, 2023

Copy link
Contributor

@AlexBVolcy AlexBVolcy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left this comment a few times, but saw it in many other places, but could you remove any blank lines from the beginning of functions?

config/config.go Outdated Show resolved Hide resolved
exchange/exchange.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/floors.go Outdated Show resolved Hide resolved
floors/floors.go Outdated Show resolved Hide resolved
router/router.go Outdated Show resolved Hide resolved
Copy link
Contributor

@AlexBVolcy AlexBVolcy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! Close to approving, some comments about test coverage. Could you also merge with master to fix the merge conflicts? Thanks!

floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher.go Show resolved Hide resolved
floors/floors.go Show resolved Hide resolved
openrtb_ext/floors.go Show resolved Hide resolved
@AlexBVolcy
Copy link
Contributor

This looks good! I will wait for @bsardo's review before approving.

config/account.go Outdated Show resolved Hide resolved
config/account.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
config/config_test.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
floors/fetcher.go Show resolved Hide resolved
config/account.go Outdated Show resolved Hide resolved
config/account.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher_test.go Show resolved Hide resolved
floors/fetcher_test.go Show resolved Hide resolved
config/account.go Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
@hhhjort hhhjort assigned guscarreon and unassigned bsardo Jul 24, 2023
config/account.go Outdated Show resolved Hide resolved
config/account.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
@pm-nikhil-vaidya
Copy link
Contributor Author

@SyntaxNode
In the last conversation on the design implementation, we have decided that we can implement the normal go routine.
As we are going to modify the fetcher in later code optimisation i.e fetcher 2.0, can we go head with this implementation instead of changing this again. Even if we modify the implementation the code will be short lived so we can do the changes at the time of fetcher 2.0
Let me know your thoughts on this

@SyntaxNode
Copy link
Contributor

@SyntaxNode In the last conversation on the design implementation, we have decided that we can implement the normal go routine. As we are going to modify the fetcher in later code optimisation i.e fetcher 2.0, can we go head with this implementation instead of changing this again. Even if we modify the implementation the code will be short lived so we can do the changes at the time of fetcher 2.0 Let me know your thoughts on this

Yes. That sounds perfect. We'll keep in close contact on the design of Fetchers 2.0 in the Fall to ensure it has good performance at scale, and investigate the worker pool proposal.

@hhhjort
Copy link
Collaborator

hhhjort commented Jul 31, 2023

Please fix the merge conflict.

@pm-nikhil-vaidya
Copy link
Contributor Author

Modified the code which addresses below points

  1. Switched from go-cache to freecache
  2. introduced httpclient and its config instead of using default http client
  3. Used time interface from timeutil
  4. introduced metrics engine instance in fetcher

Copy link
Contributor

@AlexBVolcy AlexBVolcy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! Will wait for @guscarreon before approving.

floors/fetcher.go Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
floors/fetcher.go Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
config/account.go Show resolved Hide resolved
floors/fetcher.go Outdated Show resolved Hide resolved
@guscarreon
Copy link
Contributor

Aside from the last comment above, we also need to address the merge conflicts.

@pm-nikhil-vaidya
Copy link
Contributor Author

pm-nikhil-vaidya commented Oct 4, 2023

@guscarreon
Added retry mechanism if URL is long dead

Max retries are configurable through config file

type PriceFloorFetcher struct {
	HttpClient HTTPClient `mapstructure:"http_client"`
	CacheSize  int        `mapstructure:"cache_size_mb"`
	Worker     int        `mapstructure:"worker"`
	Capacity   int        `mapstructure:"capacity"`
	MaxRetries int        `mapstructure:"max_retries"`
}

This config is consumed when floor fetcher is initialised

floorFetcher := PriceFloorFetcher{
		pool:            pond.New(config.Fetcher.Worker, config.Fetcher.Capacity, pond.PanicHandler(workerPanicHandler)),
		fetchQueue:      make(FetchQueue, 0, 100),
		fetchInProgress: make(map[string]bool),
		configReceiver:  make(chan fetchInfo, config.Fetcher.Capacity),
		done:            make(chan struct{}),
		cache:           freecache.NewCache(config.Fetcher.CacheSize * 1024 * 1024),
		httpClient:      httpClient,
		time:            &timeutil.RealTime{},
		metricEngine:    metricEngine,
		maxRetries:      config.Fetcher.MaxRetries,
	}

The worker will try to fetch the floor data from url and if fetch fails then it will increment the retry count in fetchInfo else it will reset the count value

func (f *PriceFloorFetcher) worker(fetchConfig fetchInfo) {
	floorData, fetchedMaxAge := f.fetchAndValidate(fetchConfig.AccountFloorFetch)
	if floorData != nil {
		// Reset retry count when data is successfully fetched
		fetchConfig.retryCount = 0

		// Update cache with new floor rules
		cacheExpiry := fetchConfig.AccountFloorFetch.MaxAge
		if fetchedMaxAge != 0 {
			cacheExpiry = fetchedMaxAge
		}
		floorData, err := json.Marshal(floorData)
		if err != nil {
			glog.Errorf("Error while marshaling fetched floor data for url %s", fetchConfig.AccountFloorFetch.URL)
		} else {
			f.SetWithExpiry(fetchConfig.AccountFloorFetch.URL, floorData, cacheExpiry)
		}
	} else {
		fetchConfig.retryCount++
	}

	// Send to refetch channel
	if fetchConfig.retryCount < f.maxRetries {
		fetchConfig.fetchTime = f.time.Now().Add(time.Duration(fetchConfig.AccountFloorFetch.Period) * time.Second).Unix()
		fetchConfig.refetchRequest = true
		f.configReceiver <- fetchConfig
	}
}

@pm-nikhil-vaidya
Copy link
Contributor Author

@guscarreon @SyntaxNode @bsardo @hhhjort @AlexBVolcy

I have addressed all the review comments. Kindly review and let me know if anything needs to be addressed so that we can finalise this implementation.

Copy link
Contributor

@guscarreon guscarreon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After data was fetched and cached successfully, do we need to refetch? What would be the purpose? To update the floorprice?
Or, should we stop refetching and, if the cacheExpiry is met, floor info us simply removed from cache and will be refetched whenever a new request comes in?

155   func (f *PriceFloorFetcher) worker(fetchConfig fetchInfo) {
156       floorData, fetchedMaxAge := f.fetchAndValidate(fetchConfig.AccountFloorFetch)
157       if floorData != nil {
158           // Reset retry count when data is successfully fetched
159           fetchConfig.retryCount = 0
160  
161           // Update cache with new floor rules
162           cacheExpiry := fetchConfig.AccountFloorFetch.MaxAge
163           if fetchedMaxAge != 0 {
164               cacheExpiry = fetchedMaxAge
165           }
166           floorData, err := json.Marshal(floorData)
167           if err != nil {
168               glog.Errorf("Error while marshaling fetched floor data for url %s", fetchConfig.AccountFloorFetch.URL)
169           } else {
170               f.SetWithExpiry(fetchConfig.AccountFloorFetch.URL, floorData, cacheExpiry)
171           }
    +         
    +         return
    +         
172       } else {
173           fetchConfig.retryCount++
174       }
175  
176       // Send to refetch channel
177       if fetchConfig.retryCount < f.maxRetries {
178           fetchConfig.fetchTime = f.time.Now().Add(time.Duration(fetchConfig.AccountFloorFetch.Period) * time.Second).Unix()
179           fetchConfig.refetchRequest = true
180           f.configReceiver <- fetchConfig
181       }
182   }
floors/fetcher.go

Thoughts?

@guscarreon
Copy link
Contributor

Also what should be default value for retries?

I'm sorry I missed your question. Sure, 10 is fine

@pm-nikhil-vaidya
Copy link
Contributor Author

The refetch mechanism is mainly introduced as this hosted floor data will be updated by ML algorithms , ML will constantly learn by applying particular floor value to request and based on bids it will increase or decrease the floor value, so yes we will require refetch mechanism.

@pm-nikhil-vaidya
Copy link
Contributor Author

PRD also mentions about refetch mechanism
https://docs.google.com/document/d/1YAt00RfQaU2Ugciqw1LnirLgjyUz9ibfyjfxWUHR_P0/edit#heading=h.yglzagui7gzp

guscarreon
guscarreon previously approved these changes Oct 15, 2023
Copy link
Contributor

@guscarreon guscarreon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing my feedback @pm-nikhil-vaidya . LGTM

@SyntaxNode
Copy link
Contributor

Prebid Server 2.0 has been released and Go Module name has changed from github.com/prebid/prebid-server to github.com/prebid/prebid-server/v2, per Go versioning conventions.

Please merge the master branch and update all prebid-server import statements to reference the v2 name change. Thank you.

@pm-nikhil-vaidya
Copy link
Contributor Author

@SyntaxNode @guscarreon

Updated to V2

Copy link
Contributor

@guscarreon guscarreon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@AlexBVolcy AlexBVolcy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@bsardo bsardo changed the title Floors dynamic fetch Floors: Dynamic fetch Nov 16, 2023
@bsardo bsardo merged commit fb94284 into prebid:master Nov 16, 2023
5 checks passed
svamiftah pushed a commit to sovrn/prebid-server that referenced this pull request Nov 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants