myexperiment-hackers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[myexperiment-hackers] [2012] branches/invitation_throttling/app/control


From: noreply
Subject: [myexperiment-hackers] [2012] branches/invitation_throttling/app/controllers: Throttling.
Date: Fri, 28 Nov 2008 09:19:14 -0500 (EST)

Revision
2012
Author
alekses6
Date
2008-11-28 09:19:13 -0500 (Fri, 28 Nov 2008)

Log Message

Throttling. Fully implemented the generic limiting functionality (still subject to testing).

Modified Paths

Diff

Modified: branches/invitation_throttling/app/controllers/application.rb (2011 => 2012)


--- branches/invitation_throttling/app/controllers/application.rb	2008-11-27 19:03:12 UTC (rev 2011)
+++ branches/invitation_throttling/app/controllers/application.rb	2008-11-28 14:19:13 UTC (rev 2012)
@@ -39,49 +39,91 @@
   end
   
   
-  # 
+  # the single point in the application governing validation of all features that have
+  # limited allowance of usage (e.g. 5 messages a day, etc)
   def check_activity_limit(contributor, limit_feature, update_counter=true)
+    time_now = Time.now
+    
     if (limit = ActivityLimit.find(:first, :conditions => ["contributor_type = ? AND contributor_id = ? AND limit_feature = ?", contributor.class.name, contributor.id, limit_feature]))
       # limit exists - check its validity
-      if (limit.limit_frequency && limit.reset_after && Time.now > limit.reset_after)
-        # "reset_after" / "limit_frequency" are not NULL - so the limit is periodic; now it's the time to
-        # reset the counter to zero - no matter what its value of was before
-        #
-        # before this check if the contributor also needs to be "promoted" to the next level -
+      if (limit.limit_frequency && limit.reset_after && time_now > limit.reset_after)
+        # "reset_after" / "limit_frequency" are not NULL - so the limit is periodic;
+        # now it's the time to reset the counter to zero - no matter what its value was before
+        # (this will never be executed for non-periodic counters)
+        limit.current_count = 0
+        limit.reset_after = time_now + limit.limit_frequency.hours        
+
+        # also check if the contributor needs to be "promoted" to the next level --
         # e.g. in the first month of membership on myExperiment one can send 10 messages daily,
         # but after that can send 15 (because the user becomes more "trusted")
-        if (limit.promote_after && Time.now > limit.promote_after)
-          # TODO: do the user promotion to the next level
-        end
-        limit.current_count = 0
-        limit.reset_after = Time.now + limit.limit_frequency.hours
-      end
+        if (limit.promote_after && time_now > limit.promote_after)
+          absolute_max_limit_value = eval("#{limit_feature.upcase}_LIMIT_MAX_VALUE")
+          limit_increment_value = eval("#{limit_feature.upcase}_LIMIT_PROMOTE_INCREMENT")
+          promote_every = eval("#{limit_feature.upcase}_LIMIT_PROMOTE_EVERY")
+          
+          if limit_increment_value
+            if absolute_max_limit_value
+              # absolute max value set -->
+              # increase the limit only if not exceeded absolute maximum just yet
+              if (limit.limit_max < absolute_max_limit_value)
+                limit.limit_max += limit_increment_value
+                limit.promote_after = (promote_every ? (time_now + promote_every.days) : nil)
+               else
+                # absolute maximum already reached / exceeded, disable further promotions
+                limit.promote_after = nil
+              end
+              
+              # make sure that it's not set to exceed the absolute maximum
+              # (which can happen if increment is not factor of the absolute maximum value)
+              if (limit.limit_max > absolute_max_limit_value)
+                limit.limit_max = absolute_max_limit_value
+              end
+            else
+              # absolute value not set --> simply increment
+              limit.limit_max += limit_increment_value
+              limit.promote_after = (promote_every ? (time_now + promote_every.days) : nil)
+            end
+          else 
+            # increment not set - this will be a one-time promotion
+            # (if the absolute max value is set - set limit to it; if not - the feature becomes unlimited)
+            limit.limit_max = absolute_max_limit_value
+            limit.promote_after = nil
+          end   
+        end # END of PROMOTION code
+        
+      end # END of COUNTER RESET code
+      
     else
       # limit doesn't exist yet - create it, then proceed to validation and saving
-      time_now = Time.now
+      limit_frequency = eval("#{limit_feature.upcase}_LIMIT_FREQUENCY")
+      promote_every = eval("#{limit_feature.upcase}_LIMIT_PROMOTE_EVERY")
+      
       limit = ActivityLimit.new(:contributor_type => contributor.class.name, :contributor_id => contributor.id,
                                 :limit_feature => limit_feature, 
-                                :limit_max => eval("#{limit_feature.upcase}_LIMIT"),
-                                :limit_frequency => eval("#{limit_feature.upcase}_LIMIT_FREQUENCY"),
-                                :reset_after => time_now + eval("#{limit_feature.upcase}_LIMIT_FREQUENCY").hours,
-                                :promote_after => time_now + eval("#{limit_feature.upcase}_LIMIT_PROMOTE_EVERY").days,
+                                :limit_max => eval("#{limit_feature.upcase}_LIMIT_START_VALUE"),
+                                :limit_frequency => limit_frequency,
+                                :reset_after => (limit_frequency ? (time_now + limit_frequency.hours) : nil),
+                                :promote_after => (promote_every ? (time_now + promote_every.days) : nil),
                                 :current_count => 0)
     end
     
     
-    # the action takes place - increment the counter and carry out
-    # the actual check on the current counter value
-    current_count_before_update = limit.current_count 
-    limit.current_count += 1 if update_counter
-    limit.save
-    
+    # decide if the requested action is allowed - check on the current counter value
+    action_allowed = true
     if limit.limit_max
-      # allow the action if the "current_count" has not exceeded the "limit_max" value
-      return [(current_count_before_update < limit.limit_max), (limit.reset_after - Time.now)]
-    else
-      # NULL in "limit_max" means unlimited allowance
-      return [true, (limit.reset_after - Time.now)]
+      # (NULL in "limit_max" would mean unlimited allowance - not this case)
+      # deny the action if the "current_count" is equal / exceeded the "limit_max" value
+      action_allowed = false if (limit.current_count >= limit.limit_max)
     end
+    
+    # update counted for the "current" action
+    if action_allowed && update_counter
+      limit.current_count += 1
+    end
+    limit.save # saves all changes (including counter resets, etc)
+    
+    
+    return [action_allowed, (limit.reset_after - time_now)]
   end
   
   

Modified: branches/invitation_throttling/app/controllers/messages_controller.rb (2011 => 2012)


--- branches/invitation_throttling/app/controllers/messages_controller.rb	2008-11-27 19:03:12 UTC (rev 2011)
+++ branches/invitation_throttling/app/controllers/messages_controller.rb	2008-11-28 14:19:13 UTC (rev 2012)
@@ -161,6 +161,9 @@
         format.html { redirect_to messages_url }
       elsif !sending_allowed
         # when redirecting, the check will be carried out again, and full error message displayed to the user
+        # (this is an unlikely event - can only happen when the user opens several "new message" pages one
+        #  after another and then posts messages from each of them, rather than opening a new one for each
+        #  message - therefore, it will not have significant performance effect on running the allowance check again)
         format.html { redirect_to new_message_url }
       else
         format.html { render :action ="" "new" }

reply via email to

[Prev in Thread] Current Thread [Next in Thread]