Search Postgresql Archives

Design / Implementation problem

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

 



This is possibly not a DB only problem, the solution may involve application logic as well. But PG users
are the smartest bunch I know. Ass kissing aside, here are the details:

*** The Scenario ***

We are running a customer loyalty program whereby customers earn points for purchasing products. Each product has a value of points that are earned by purchasing it, and a value of points required to
redeem it.

In order to prevent customers from stockpiling points, we want to place an expiry date on points so that unused points expire and are lost if they are not redeemed within a certain period of time. This will be calculated on a FIFO basis, I.e., the oldest points will expire first.

We will assume the expiry period is 12 months.

*** The Problem ***

Ascertaining which points to expire is fairly conceptually simple. At any given point in time, the points expired is simply the balance on hand at the start of the period, less redemptions in that period. If the redemptions is less than the balance at open, not all points that were available on
that date were used, and the difference is the expiry.

This can be done periodically, say, at the start of every month. However there are a few problems
with doing it periodically

1. The runs are likely to be too large to be manageable. A DB with tens of thousands of customers and many hundreds of thousands or even millions of sales in the records tables will require several queries and some application calculation to compute. If it takes 2 seconds to compute each balance of a 20,000 strong customer base, that's over 11 hours of heavy lifting in the DB, which will likely result in severely degraded performance during those hours. This problem can only get worse as time goes on, and hardware upgrade requirements just to accommodate a 12 hour window
  once a month is the sign of an app not designed to scale well.

2. Calculating the balance on the fly would be more effective, as it is unlikley that many customers will check their balance on a regular basis. It is likely that a small fraction of customers will check their balance in a given month, meaning that calculating it on the fly would both spread the load over time as well as reduce the total load, even if on the fly calculation results in
  significantly higher per-customer calculation time.

3. The app is a web app, and it would be preferable to contain business logic within the database itself or the current app codebase. Spreading application logic into an external mechanism such as cron or an external daemon would be undesirable unless there was no other way.

*** A Possible Solution ***

Calculating the balance on the fly can be done easily if it is done at the time the customer seeks to redeem a voucher. Expired points are only relevant at these times, and so the expired points
would be calculated with an application function that did the following:

1. Get the balance as it was 12 months ago by getting total points earned less redemptions and expiries
  up to that date.
2. Subtract from it redemptions and expiries since then. The value obtained, if it is positive, is the
  value of points to expire.
3. Log the expiry entry, and then calculate the balance of points to the current date by subtracting
  total points redeemed and expired from total points earned.

This procedure has a few practical problems, however:
1. Customers, when they view their running total, will not be aware that some of the points included in it will have expired, as the expiry will only happen when the application attempts to log a
  redemption.
2. A customer may attempt to redeem a product they do not have enoughh points for, and be told at
  the last minute that they do not have enough points, leading to acrimony.

The solution is then to calculate it on only on redemptions, but also whenever the customer attempts to view their balance. This will ensure that expired points will never be shown in the current balance of available points. This, however, means the calculation may be done many times by each customer in
a single catalog browsing session.

*** The Question ***

Is there a way to design the DB schema as well as the query in such a manner that calculating the point balance on the fly is not an unfeasibly heavy duty calculation to be done at every page view?

This problem does not appear to be solved comprehensively by anyone. When I log into my credit card company web site to check my points, I get a message "please come back in an hour, and your points will be calculated" if I haven't logged in for over a week. So obviously they calculate the balance
and put it in a table that acts as a "cached balance".

Emirates has a different solution, they do bi-annual runs, so points expire every March and September
for them.

Neither of these solutions appeals to me, and there must be A Better Way(tm).


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Postgresql Jobs]     [Postgresql Admin]     [Postgresql Performance]     [Linux Clusters]     [PHP Home]     [PHP on Windows]     [Kernel Newbies]     [PHP Classes]     [PHP Books]     [PHP Databases]     [Postgresql & PHP]     [Yosemite]
  Powered by Linux