Avatar of MFredin
MFredin
Flag for United States of America asked on

Banner rotation system

I'm looking for help on a banner rotation system.  I have 5 ads (could be more) that I am displaying all at once in a vertical column on my home page.  I want to give each banner an equal share of space near the top.

How can I rotate them equally without displaying the same ad twice on the page?
ColdFusion Language

Avatar of undefined
Last Comment
_agx_

8/22/2022 - Mon
gdemaria

Did you want the ads to show evenly for each user?

For example, you could create a session variable and increment it each time you show an ad..

<cfset session.adNumber = session.adNumber + 1>
... show add number session.adNumber...

<cfif session.adNumber gt maxAdNumber>
    <cfset session.adNumber = 1>
... reset to first ad

Or you can have a strategy to show the ads evenly across all users?   In that case, you can use a database level counter (so a field on the add table tells you which to show).

Of course there is a random number generator that you could trust will be even over a large number of displays...

MFredin

ASKER
Thanks gdemaria,

I'm trying to show the ads in a rotating position evenly across users.  The main point is so the ad on the bottom doesn't always show up on the bottom... eventually it will be displayed on the top.  Just trying to be fair to all my advertisers.
gdemaria


assuming you have your ads stored in a database table, each time you display an ad, update the record with a listDisplayedDate = getDate()

Then, you can always fetch the ad with the oldest lastDisplayedDate to display...

select top 1 * from myAds order by lastDisplayDate asc

Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
MFredin

ASKER
Thanks gdemaria,

The only problem is, I only have 5 ads and they are always going to be displayed together.  What I'm really only worried about is their position.  If ad1 was displayed at the bottom position last time, it should be moved up next time, etc.  It's a vertical column of ads.
gdemaria

ugh, ok...   not sure how you are determining the position of the ad, but let's say you have them in the database.

A column with a simple number identifies their position where 1 is the top spot and 5 is the bottom spot.

<cfset maxPos = 5>  <!---- the number of ads ---->

update myBannerAds
   set  displayPos = case when displayPos = #maxPos# then 1 else displayPos + 1 end


If you run this update each time you display your page with ads, then you will rotate the ads effectively with every display.
_agx_

I don't know. I'm thinking session variables might produce more even results.  Because it seems like the db update would be subject to race conditions.  At least it would under heavy load.

user 1 => updates positions                 [5,1,2,3,4]
user 2 => updates positions again       [1,2,3,4,5]
user 1 => reads positions and gets [1,2,3,4,5]     ** prev. ordering [5,1,2,3,4] is skipped
user 2 => reads positions and gets [1,2,3,4,5]

Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
gdemaria


yes, that's true.  I could see a sequence being skipped occassionally but really very rarely (two processes hitting the table so closely to split a transaction)

it's not conventional, but you could do the update an select in a single cfquery..
<cfquery name="getAds"...>
  update ...

  select ...
</cfif>


But the session variables are per user, that's why I thought database to make it even between users.   Perhaps an application scope variable would satisfay both, although I don't love the idea of updating an application scope variable so frequently?  Not sure why though :)

_agx_

>> but really very rarely (two processes hitting the table so closely to split a transaction)

Not necessarily. All you need is any delay to occur in between the time user 1 updates the information and reads it back.  That's entirely possible in busy applications.

>> I don't love the idea of updating an application scope variable

Perhaps for the same reason I don't love the db idea here: race conditions ;-) You'd have to lock the application scope (exclusive/single thread) every time you updated.  Even if it's only for a split second.

Instead I'd store the banners in the application scope and toggle a "row" number stored in a session variable.  The locking is a non-issue.
gdemaria


> Instead I'd store the banners in the application scope and toggle a "row" number stored in a session variable.  The locking is a non-issue.

yup, agreed.  As long at the asker is ok with a little less even rotation among ads.  If determinging the ad by-user,  the user is not likely to browse a multipe of 5 pages (5,10,15) thus the ads per user will not be even.   The way around this would be to alter the initial ad order at the start of each session.

For example, if each user starts their rotation with 1,2,3,4,5... that puts ad #1 at the top every time and will always get more exposure. If the user views only 3 pages, ad 4 and 5 won't get to the top.  

The way around this would be to alter the starting sequence of the ads, first user 1,2,3,4,5; then 2nd user 2,3,4,5,1;  third user 3,4,5,1,2; etc.   This would give each ad an equal starting point.  But then how do you determine the starting ad for each user?  Same problem, isn't it?

In summary, I think that the asker is faced with a choice between occasionally skipping a sequence or the challenge of an even balance between ads.   Either approach would give a pretty good rotation, but not perfect, depending on how exact you need to be...


All of life is about relationships, and EE has made a viirtual community a real community. It lifts everyone's boat
William Peck
_agx_

>> But then how do you determine the starting ad for each user?  Same problem, isn't it?

One option is randomizing the starting row: startRow = randRange(1,5) ;-).  That would prevent one banner from always getting more exposure.  It doesn't guarantee absolute even distribution across the ads. But neither does the db method since it could potentially skip multiple times on a busy site.  
Granted for a a site with very low concurrency, the db method would produce a more "even" rotation.

>> Either approach would give a pretty good rotation, but not perfect, depending on
>> how exact you need to be...

Yep.  The only bullet proof approach is using the application scope with an exclusive named lock

         <cflock ...>
                 <cfset application.startPos = (application.startPos = maxNum ? 1 : application.startPos + 1)>
                 <cfset request.startPos = application.startPos >
        </cflock>
gdemaria


Yup, agree.

Personally, I would choose either the random start session variable approach or the database sequencing approach over the application scope option.


Btw, when did this conditional format start working in CF (non-cfscript) ?  

   = (application.startPos = maxNum ? 1 : application.startPos + 1)>
SOLUTION
_agx_

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
MFredin

ASKER
Thanks guys, here's what I've tried...

<cfset maxPos = 7>  <!---- the number of ads ---->
      
<!--- Get Ads --->
<cfquery name="ads">
      SELECT display_position, banner_id
      FROM banners
      WHERE display_position IS NOT NULL
</cfquery>      
            
<cfoutput query="ads">
      
<cfquery>
UPDATE banners
SET display_position =  case when display_position = #maxPos# then 1 else #ads.display_position# + 1 end
WHERE banner_id = #ads.banner_id#
</cfquery>

<cfmodule template="Advertising/bannerAd.cfm" homepage_ad="1" homepage_position="#ads.display_position#">
<br />
</cfoutput>


The problem is, very once in a while I somehow end up with duplicate display position in my banners table.  It throws everything off.

Making sure the ads are rotated evenly isn't important... I just want to be sure they rotate somewhat.
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
ASKER CERTIFIED SOLUTION
Log in to continue reading
Log In
Sign up - Free for 7 days
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
MFredin

ASKER
Thanks gdemaria, that's perfect!  

How do you guys suggest I break up the points on this one?  gdemaria provided the best solution but agx added great insight as well.
_agx_

I'd give the lion's share to gdemaria, because he provided the ultimate solution and the bulk of the code :)