How to distribute new tokens (fee) between token holders - ethereum

Good morning everyone.
I have a problem, I'm thinking about it but the only solution I found at the moment is very expensive.
Imagine that we have a token, we know all the holders of this token. And now just for having this token, we want to give you even more tokens. For example if your balance today was higher than 0, at the end of the day you have your balance + "X" that the system gives you as a reward for logging the token.
On the other hand "X" is not the same for all holders, sistema earns "Y" every day, then every 24 hours this "Y" has to be distributed among all holders depending on their amount of tokens in the balance, the more tokens you have, the bigger your daily reward will be.
For example:
User 1 has 10 tokens.
User 2 has 90 tokens.
24 hours pass, system has earned 10 tokens.
System distributes these 10 tokens between 2 holders:
User 1 receives 1 token. His balance will be 11 tokens.
User 2 receives 9 tokens. His balance will be 99 tokens.
The only way I see now as a solution is to create a for to go through all the holders, but if we have a lot of holders, the cost of this operation will be huge and it has to be in sections, because if we have 20000 holders we can't go through them in the same for, we will run out of gas.
So what I have thought is to create a function to which we pass the index range of the holders and how many tokens we have to distribute among them. And so call it several times, until everything is distributed. As far as I understand it is the worst solution to the problem. Any other ideas?

If gas cost is the issue. Let users claim their reward rather than you send it? Lots of ICO's use Merkle distributors for this reason. So you would wrap your logic around Merkle proofs and keep updating the Merkle root every 24 hrs.
Here are some resources:
https://collectednotes.com/cibrax/using-merkle-trees-for-bulk-transfers-in-ethereum
https://github.com/Uniswap/merkle-distributor

Related

Is it possible to predefine a winning number in Solidity and make it visible after some condition?

I want contract to predifine winning boxes, is it possible to achieve this functionality solidity, so no one can detect which box is the winning box?
If so, how can I predefine winning boxes which will be unreadeable until the user opens a specific box?
I tried to use block.timestamp to randomly choose winning boxes upon contract creation, but hacker can replicate block.timestamp and determine which box is the winning box.
If you have a trusted party, you can hash the winning number (with a salt so people don't brute force it), then reveal it after a certain point. This has an obvious disadvantage that whoever hashed the number knows the winning number.
Your question implies you are looking for randomness, so you can used established randomness patterns like commit reveal or chainlink VRF. With commit reveal, when someone plays the game, they commit that a future blockhash will be the random number. A block producer can tamper with the hash to a certain degree, so if you are worried about that, use chainlink VRF.
Make your boxes to be the Schrödinger boxes - winners and losers at the same time. When a user selects a box and sends the Transaction to your contract - request a random number from Chainlink and make it to a winner or loser box.

How do reflection tokens distribute transactions fee to everyone wallets without doing an insane amount of transactions and running up alot of gas fee

How do reflection tokens distribute transactions fees to everyone's wallets without doing an insane amount of transactions and running up alot of gas fees while at the same time giving them more tokens according to how large their position is
For reflection tokens everyones balance is a formula, not a fixed integer value.
Simplified example:
User A buys 10 tokens.
Current Reflection multiplier = 1.0
getBalance(user A) equals 10 * 1.0 = 10
Something happens which increases the reflection multiplier to 1.1
getBalance(user A) equals 10 * 1.1 = 11
The users balance changes without any transfer transactions having to be done.

How to design a relational model for double-entry accounting with job costing

I would like to commend to readers the answers here and here for the depth and thought that went into them. I stumbled across them while searching for something tangential for a project I'm working on, and I got caught up reading them from top to bottom.
I am trying to build a niche-market app using these principles (namely, double-entry accounting), with job-costing thrown in. The above answers have been extremely helpful in reshaping my concept of what both the accounting and the database-ing should look and work like. However, I'm having a hard time integrating the job-costing portion of the equation into the excellent graphical examples that were provided.
There were several transaction examples using the House, account holders, fees, etc. I have a few other specific use-cases I would love to get some input on:
I have no customers. I buy a property (usually cash goes out, a liability (loan) is created, an asset (the property) is created), spend a bunch of money to fix it up (either cash out at a store, credit card charges at a store, or a check written to a vendor, which debits the property asset and debits or credits the funding source), and then sell it (cash comes in, the loan is paid off, and hopefully there's more cash left than what I spent on the project). This likely creates more ledger entries than I've listed above, but I'm not an accountant. I think I understand that all my costs go toward my basis in the property, and if my net proceeds are greater than my basis, then I've made money, and if not, then not.
So what I need to record are expenses that a) come from a specific account (i.e. company checking account or owner's Best Buy card etc.), b) are generally associated with a specific job (but not always - I do have the occasional overhead expense like office supplies), and c) are always associated with a cost code (i.e. '100.12 - Window Materials', '100.13 - Window Labor', etc.).
Frequently I receive bills from vendors that are due sometime in the future. I would like to track the bills received but not-yet-paid for a given job (committed costs). I think this transaction looks like this, but I'm not really sure:
As you may have surmised from my quip above about the "owner's Best Buy card," I sometimes (more often than I should) use my personal funds for company- and job-related expenses. I think (again with the caveat that I'm a layman) that all of those expenditures credit "Owner's Equity," and debit/credit other accounts as needed.
I've been keeping track of all of this in a big, ugly spreadsheet, which is why I'm trying to build an app to replace it - the spreadsheet method doesn't work very well and it certainly won't scale.
Preliminary
For those reading this Answer, please note that the context is as follows, in increments:
Derived Account Balance vs Stored Account Balance
Relational Data Model for Double-Entry Accounting
If you have not availed yourself to that, this Answer may not make sense.
I will respond in a sequence that is Normalised, which is of course different to the way you have laid out the problem.
Principle & Correction
There are a few, more than one, errors in your stated problem which you are not aware of, so the first step is awareness; understanding. Once a problem is correctly and precisely declared, it is easy to solve. These are errors that developers commonly make, so they need to be understood as such ... long before an app is contemplated.
1 First Principle
I've been keeping track of all of this in a big, ugly spreadsheet [the spreadsheet method doesn't work very well and it certainly won't scale], which is why I'm trying to build an app to replace it
If the manual (or the previous computerised) system is broken, and you implement a new or replacement app that is based on it, you are guaranteed to carry that broken-ness into the app.
Worse, if this is not understood, a third app can be written, promising to fix the problems in the second app, but it too, is guaranteed to migrate the problems that were not fixed in the first and second app.
Therefore, you must identify and correct every single problem in the system that you are replacing, including testing, before you can design an app and database that has any chance of success.
Scaling is the least of our worries. How any particular thing works with any other thing is the problem.
The fact that you have one great big ugly spreadsheet means that you have an overall perspective: humans can do that, we can fly by the seat of our pants, but computers cannot, they require explicit instructions.
2 Second Principle
I've been keeping track of all of this in a big, ugly spreadsheet [...] - the spreadsheet method doesn't work very well
Why does it not work [as it stands] ?
Reason 1 of 2.
You make a mistake that developers commonly make: you inspect and study the the bits and pieces of a thing, which is in the physical realm, and try to figure out how the thing works. Guaranteed failure, because how a thing works; its purpose; etc, is in the intellectual realm, not the physical.
I won't detail it here, but the larger problem must be noted. This error is a specific instance of a larger error, and very common, that:
developers focus on the functions of the GUI,
instead of the demand, which is to
correctly define the data and its relations, upon which the functions of the GUI are existentially dependent.
A person who has not learned about internal combustion, cannot figure out how to build an engine from looking at the parts of an engine that has been taken apart, even if the parts are laid out carefully. Let alone one with injectors or turbo-chargers. The principle of internal combustion is logical, the parts are physical.
Here you have looked at the spreadsheets that others have used to do their Accounting, and perhaps copied that, without understanding what they are doing with the spreadsheets.
Case in point.
You have examined the first and second linked Answers, and you think you can figure out how to apply that to a new app that fixes the dirty big spreadsheet problem.
Many developers think that if they work out the nuts and bolts, copy-paste-and-substitute, somehow the app will work. Note the carefully thought-out, but still incomplete, graphics that details perceived transactions.
They are missing the logical realm, and messing with the physical realm without the demanded understanding of what they are messing with.
In a word, forget about the pretty graphics for the Transactions, both yours and mine, and seek to understand the Logic (this principle) and the Accounting Standard [3].
"Test driven development" aka "code the minimum" aka "trial and error" is a totally bankrupt method, it has no scientific basis (marketing, yes, but science, no), and it is guaranteed to fail. Dangerous, because the cost is ongoing, never finite.
And to keep failing, if you understand the above.
More precisely, it is anti-science, in that it contradicts the science for building apps and databases.
So the first step is to break that great big spreadsheet down into logical units that have a purpose. And certainly, link each referencing spreadsheet column to the right columns in the referenced spreadsheet ... such that any Amount value is never duplicated.
3 Third Principle
I've been keeping track of all of this in a big, ugly spreadsheet [...] - the spreadsheet method doesn't work very well
Why does it not work, either as it stands, or when the spreadsheet has been divided into logical units ?
Reason 2 of 2.
Lack of Standards.
Since the subject matter is Accounting, we must use Accounting Standards.
That single great big ugly spreadsheet is ready evidence that you have not used an Accountant to set it up. And of course, you cannot set up a set of spreadsheets to do your Accounting without either understanding Accounting or using a qualified Accountant.
Therefore the second step is to either get an Accountant, or obtain a good understanding of Accounting. Note again, the ready evidence of your carefully thought out transactions: despite the fact that you are a very capable person, you cannot figure out the Accounting logic that is in the first and second linked Answers, let alone the Accounting that you need for your app (or your manual system).
So the best advice I can give you is, as stated in the Double-Entry Accounting Answer, find some good Tutorials on the web, and study them.
If you did that, or hired an Accountant to set up your books, you would split the single big fat spreadsheet into standard Accounting Spreadsheets:
Balance Sheet:
Asset or Liability
Profit & Loss:
Revenue or Expense
and one more set (later)
Another way of stating this principle is this. When one is ignorant that a Standard exists, or worse, when one knowingly chooses to not comply with it, one is left in the dangerous position of re-inventing the wheel, from scratch. Aka "Test driven development", aka "code the minimum possible", aka "trial and error". That means that one will go through an entire series of increments of development, which can be eliminated by observance of the Standard.
Problem & Solution
Now that we understand the principles, we can move on to determination of the specific problems, and their solutions. Each of these is a specific application of the Third Principle.
4 Property/Mortgage Treatment
I have no customers. I buy a property (usually cash goes out, a liability (loan) is created, an asset (the property) is created), spend a bunch of money to fix it up (either cash out at a store, credit card charges at a store, or a check written to a vendor, which debits the property asset and debits or credits the funding source), and then sell it
I am not saying that you have not heeded the advice I have given in the Double-Entry Answer. I am saying you have not appreciated the gravity of the advice; what it means in an Accounting context (before we venture into the database context).
Money represents value. Money; value, cannot be created or destroyed. It can only be moved. From one bucket to another. The demand is to have your buckets defined and arranged properly, according to [3].
The property is not created, it already exists. When you buy a property, there is a movement of your cash to the bank, and a movement of their property to you. In the naïve sense only, the property is now an "asset", the mortgage is now a "liability". That naïveté will be clarified into proper accounting buckets later.
You are, in fact, operating as a small single-branch bank; a cooperative; a casino. The precise context for the Double-Entry Accounting Answer. The following is true for
either a corrected set of spreadsheets,
or for following and implementing the Double-Entry Accounting Answer (if you go directly into the app ... without testing the correction to your single spreadsheet).
This is really important to understand, because it has to do with legislation in your country, which you have not mentioned. That legislation will be known to you as Taxation, or your Tax Return for the business. Even if you hold just one property at any one time.
Your "customer" is each bank that is engaged for each property. Name it for the property.
Each mortgage (property) should be set up as an External Account. That will allow you to conduct only those transactions that are actually related to it, against it. Loan Payments; Bank Charges; Expenses; etc. There will be no incoming money, until the property is sold.
In any case, the External Account will match the Bank Statement that the bank gives you for the mortgage account (which you did not mention, but which is a fundamental requirement of Accounting).
As defined in the Double-Entry Accounting Answer, every transaction on an ExternalAccount will have one Double-Entry leg in the Ledger. More, later.
Whether it is an Asset or a Liability in Accounting terms, is a function of the Ledger entry, not a function of the External Account. (By all means, we know it represents a property, which by a naïve perspective is an "asset", until it starts losing money, when it by naïve perspective, becomes a "liability".)
Another way of defining this point is, the bank loan represents a contract, upon which money (value) "changes hands" (is moved). The bank which you engaged is the "customer", the External Account. You must keep all income and expense related to the contract, with the contract.
niche-market app ...
I have a few other specific use-cases ...
No, you don't. There is nothing new under the sun. If you set up your books correctly (multiple linked spreadsheets using Accounting Standards), this is a vanilla use case. Hopefully my explanation has demonstrated that fact.
5 Ledger
Where the above points have to do with the intellectual realm, the understanding of each problem and therein the solution, which causes little work in the physical realm, this point, which has the same demand for the intellectual, is onerous at the physical level. That is, the number of keystrokes; checking; changes; checking ... before you get it set up correctly.
Although the first linked Answer deals with:
Derived vs Stored Account Balance (efficient and audit-able processing re month end),
and the second linked Answer deals with:
Double-Entry Accounting (implementation of an over-arching Accounting Standard in an existing Accounting system, a higher level of audit-ability),
neither explains the Ledger in detail.
The Ledger is the central article of any Accounting system.
The Double-Entry system is not a stand-alone article, but an advancement to that Ledger.
The data model is the specific how to set the database up correctly for both the app, and any reporting client s/w to use, uneventfully.
You do not have a true Ledger. The single big spreadsheet is not a Ledger.
You must set up the Ledger, according to [3]. At best, some of the items in that spreadsheet will be entries in the Ledger, but note, you will perceive them quite differently, due to the corrections set forth in [1][2][3].
Note that when we say "put that in the Ledger" or "that is not in the Ledger", which is for simplicity, what we mean precisely is a reference to single Ledger Entry, which is identified by a specific Account Number in the Ledger.
In the data model, this is LedgerNo.
Likewise, when we say "Accounts", we mean precisely a single Account Number in the Ledger.
If a transaction is not in the Ledger (a specific Account Number, a LedgerNo, one leg of the DEA Credit/Debit), it is not in the "accounts", it is not accounted for.
This is where you will set up genuine Accounts for Assets, and for Liabilities. This is for Internal purposes, in the Ledger, as declared in the margin for Internal in the data model.
The best advice I can give you is, trawl the web for Tutorials on Accounting; determine which are good; study them carefully, with a view to setting up a proper Ledger for your purposes.
The simple answer is, the Ledger is an Hierarchy of Account Numbers.
Wherein the leaf level is an actual AccountNo that can be transacted against,
and the non-leaf levels exist for the purpose of aggregation, no transactions allowed.
Whenever the Ledger is reported (or any derivative of the Ledger, such as BalanceSheet or Profit & Loss):
the hierarchy is shown by indentation,
the transactional Account entries show the Current Balance for the current month
and the aggregate Account entries show the aggregate for the tree under it
[your graphics re transactions]
First and foremost, every Transaction is in the Ledger. That means one leg of the Double-Entry Accounting Transaction is in the Ledger. Look at § 5 in my Double-Entry Accounting Answer, notice that every Business Transaction has at least one blue entry (do not worry about the other details).
Second, the other DEA leg is:
either in the Ledger, meaning that the money moved between one Ledger Account LedgerNo and another Ledger Account LedgerNo. Notice the Business Transactions where both sides are blue.
or in an External Account, meaning that the money moved between one Ledger Account LedgerNo and an External Account AccountNo. Notice the Business Transactions where one side is blue and the other is green.
When you understand that, and you have your Ledger set up, there will be no "??" in your graphics, and the blue/green will be shown. (Do not re-do your graphics, I expect that this Answer will suffice.)
Your "asset/liab" designation is not correct. More precisely, it is premature to make that declaration before the Ledger is fully defined and arranged. First set up your Ledger, with Asset/Liability for each entry in mind. Then you will not have to declare "asset/liab" on each transaction, because that is a function of the Ledger Account Number LedgerNo, not a function of the transaction.
expenses that a) come from a specific account (i.e. company checking account or owner's Best Buy card etc.),
Ledger-ExternalAccount
(one DEA leg in the Ledger, the other leg in the External Account). Noting the caveats above. The other DEA leg will be to one of these (hierarchy):
Expense/Property Improvement/Structure/Material
Expense/Property Improvement/Structure/Labour
Expense/Property Improvement/Fitting/Material
Expense/Property Improvement/Fitting/Labour
Expense/Property Improvement/Furniture
expenses that c) are always associated with a cost code (i.e. '100.12 - Window Materials', '100.13 - Window Labor', etc.).
You will no longer have "cost codes", they will all be Ledger Account Numbers LedgerNos, because the Ledger is where you account for anything and everything.
One DEA leg in the Ledger, the other leg in the External Account for the particular property. The hierarchy will be the same as the previous point.
expenses that b) are generally associated with a specific job
Ledger-ExternalAccount
(one DEA leg in the Ledger, the other in the External Account).
(but not always - I do have the occasional overhead expense like office supplies)
Ledger-Ledger
one DEA leg in the Ledger for an Expense or Liability LedgerNo ... that the money was paid to
Expense/Regular/Office Supplies
the other leg in the Ledger for a Revenue or Asset LedgerNo ... that the money was paid from
Revenue/Monthly Payable
6 Credit & Other Card Treatment
credit card charge
Best Buy card
Each of your cards represents a contract, an Account that that needs to be transacted against, that must be balanced against the monthly statement provided by the institution that issued the card.
Set up each one as an External Account, one DEA leg here, the other in the Ledger.
"owner's Best Buy card" is not clear to me (who is the owner, you or the property owner ... if the latter then the assumption thus far, that "you" buy and sell properties is incorrect.)
In any case, I believe I have given enough detail for you to figure it out.
Do not amalgamate an owner's property Account and their Best Buy card into one External Account: keep separate External Accounts for each.
7 Job Costing
Notice that I am addressing this last, because once you fix the big problems, the problems that remain, are small. What you set out as the big problems (job costing; profit/loss per property) are, once the Ledger has been set up correctly for your business, actually small problems.
As far as I can see, Job Costing is the only remaining point that I have not addressed. First, the issue to be understood here is, the difference between Actuals and Estimates. Everything I have discussed thus far are Actuals.
For Estimates, the Standard procedure is to set up a separate Account structure (tree in the hierarchy) in the Ledger. These are often called Suspense Accounts, as in money that is held in suspense.
Treated properly, these Accounts will prevent you from closing or finalising an External Account before all the Estimates have been transferred to Actuals (Suspense to zero).
The Business Transactions are exactly the same as for Actuals.
This will provide precise tracking of such figures, and also the difference when an item moves from Estimate to Actual.
8 Data Model • Job Costing
Noting that the data model in the first and second linked Answers are complete for the purpose, wherein the Ledger is not expanded:
this Answer deals with explanation of the Ledger, and this data model gives the full definition of a Ledger
Arranged by AccountType
A single-parent hierarchy
Only the leaf level LedgerAccount may be transacted against
The intermediate level LedgerIntermediate is for summarising the tree below it.
I have further Normalised Transaction
expanded External Account to show a Person vs an Organisation
All constraints are made explicit.
Obviously too large for an inline graphic. Here is a PDF in two pages:
the Data Model alone (as above)
the Data Model with sample data and notes, it includes all the examples covered in the Answer
Note the indentation in the Ledger, which denotes the Account hierarchy
Comments
How do you insert the first ledger (e.g. 100 Asset, no parent)?
The Ledger is a Tree, a Single Parent Hierarchy (aka "one way" for strange reasons), as per Account Hierarchy. A root row is required. In a database build operation (using DDL from a file), we generally do all our CREATE TABLEs, followed by all our ADD CONSTRAINT FKs. Insert the root row in with the CREATE TABLE.
After the
CREATE TABLE Ledger
do
INSERT Ledger VALUES ( 0, 0, "I", "AL", "Root", ... ).
After the
CREATE TABLE LedgerIntermediate
do
INSERT LedgerIntermediate VALUES ( 0 ).
Given that the reverse of Comprises is belongs to, all first-level Ledgers eg. Fees, House, Interbank and your Asset would belong to this root row.

Decrementing money balance stored on master server from numerous backends? (distributed counter, eh?)

I have some backend servers located in two differend datacenters (in USA and in Europe). These servers are just delivering ads on CPM basis.
Beside that I have big & fat master MySQL server serving advertiser's ad campaign's money balances. Again, all ad campaigns are being delivered on CPM basis.
On every impression served from any of backends I have to decrement ad campaign's money balance according to impression price.
For example, price per one impression is 1 cent. Backend A has delivered 50 impressions and will decrement money balance by 50 cents. Backed B has delivered 30 impressions and it will decrement money balance by 30 cents.
So, main problems as I see are:
Backends are serving about 2-3K impressions every seconds. So, decrementing money balance on fly in MySQL is not a good idea imho.
Backends are located in US and EU datacenters. MySQL master server is located in USA. Network latency could be a problem [EU backend] <-> [US master]
As possible solutions I see:
Using Cassandra as distributed counter storage. I will try to be aware of this solution as long possible.
Reserving part on money by backend. For example, backend A is connecting to master and trying to reserve $1. As $1 is reserved and stored locally on backend (in local Redis for example) there is no problem to decrement it with light speed. Main problem I see is returning money from backend to master server if backend is being disabled from delivery scheme ("disconnected" from balancer). Anyway, it seems to be very nice solution and will allow to stay in current technology stack.
Any suggestions?
UPD: One important addition. It is not so important to deliver ads impressions with high precision. We can deliver more impressions than requested, but never less.
How about instead of decrementing balance, you keep a log of all reported work from each backend, and then calculate balance when you need it by subtracting the sum of all reported work from the campaign's account?
Tables:
campaign (campaign_id, budget, ...)
impressions (campaign_id, backend_id, count, ...)
Report work:
INSERT INTO impressions VALUES ($campaign_id, $backend_id, $served_impressions);
Calculate balance of a campaign only when necessary:
SELECT campaign.budget - impressions.count * $impression_price AS balance
FROM campaign INNER JOIN impressions USING (campaign_id);
This is perhaps the most classical ad-serving/impression-counting problem out there. You're basically trying to balance a few goals:
Not under-serving ad inventory, thus not making as much money as you could.
Not over-serving ad inventory, thus serving for free since you can't charge the customer for your mistake.
Not serving the impressions too quickly, because usually customers want an ad to run through a given calendar time period, and serving them all in an hour between 2-3 AM makes those customers unhappy and doesn't do them any good.
This is tricky because you don't necessarily know how many impressions will be available for a given spot (since it depends on traffic), and it gets even more tricky if you do CPC instead of CPM, since you then introduce another unknowable variable of click-through rate.
There isn't a single "right" pattern for this, but what I have seen to be successful through my years of consulting is:
Treat the backend database as your authoritative store. Partition it by customer as necessary to support your goals for scalability and fault tolerance (limiting possible outages to a fraction of customers). The database knows that you have an ad insertion order for e.g. 1000 impressions over the course of 7 days. It is periodically updated (minutes to hours) to reflect the remaining inventory and some basic stats to bootstrap the cache in case of cache loss, such as actual
Don't bother with money balances at the ad server level. Deal with impression counts, rates, and targets only. Settle that to money balances after the fact through logging and offline processing.
Serve ad inventory from a very lightweight and fast cache (near the web servers) which caches the impression remaining count and target serving velocity of an insertion order, and calculates the actual serving velocity.
Log all served impressions with relevant data.
Periodically collect serving velocities and push them back to the database.
Periodically collect logs and calculate actual served inventory and push it back to the database. (You may need to recalculate from logs due to outages, DoSes, spam, etc.)
Create a service on your big & fat master MySQL server serving advertiser's ad campaign's money balances.
This service must implement a getCampaignFund(idcampaign, requestingServerId, currentLocalAccountBalanceAtTheRequestingServer) that returns a creditLimit to the regional server.
Imagine a credit card mechanism. Your master server will give some limit to your regional servers. Once this limit is decreasing, a threshold trigger this request to get a new limit. But to get the new credit limit the regional server must inform how much it had used from the previous limit.
Your regional servers might implement additionally these services:
currentLocalCampaignAccountBalance
getCampaignAccountBalance(idcampaign): to inform the current usage of a specific campaign, so the main server might update all campaigns at a specific time.
addCampaign(idcampaign, initialBalance): to register a new campaign
and it's start credit limit.
supendCampaign(idcampaign): to suspend the impressions to a
campaign.
resumeCampaign(idcampaign): to resume impression to a campaign.
currentLocalCampaignAccountBalance finishCampaign(idcampaign): to
finish a campaign and return the current local account balance.
currentLocalCampaignAccountBalance
updateCampaignLimit(idcampaign, newCampaignLimit): to update the limit
(realocation of credit between regional servers). This service will
update the campaign credit limit and return the account balance of
the previous credit limit acquired.
Services are great so you have a loosely coupled architecture. Even if your main server goes offline for some time, your regional servers will keep running until they have not finished their credit limits.
this may not be a detailed canonical answer but i'll offer my thoughts as possible [and at least partial] solutions.
i'll have to guess a bit here because the question doesn't say much about what measurements have been taken to identify mysql bottlenecks, which imho is the place to start. i say that because imho 1-2k transactions per second is not out of range for mysql. i've easily supported volumes this high [and much higher] with some combination of the following techniques, in no particular order here because it depends on what measurements tell me are the bottlenecks: 0-database redesign; 1-tuning buffers; 2-adding ram; 3-solid state drives; 4-sharding; 5-upgrading to mysql 5.6+ if on 5.5 or lower. so i'd take some measurements and apply the foregoing as called for by the results of the measurements.
hope this helps.
I assume
Ads are probably bought in batches of at least a couple of thousands
There are ads from several different batches being delivered at the same time, not all of which be near empty at the same time
It is OK to serve some extra ads if your infrastructure is down.
So, here's how I would do it.
The BigFat backend has these methods
getCurrentBatches() that will deliver a list of batches that can be used for a while. Each batch contains a rate with the number of ads that can be served each second. Each batch also contains a serveMax; how many ads might be served before talking to BigFat again.
deductAndGetNextRateAndMax(batchId, adsServed) that will deduct the number of ads served since last call and return a new rate (that might be the same) and a new serveMax.
The reason to have a rate for each batch is that when one batch is starting to run out of funds it will be served less until it's totally depleted.
If one backend doesn't connect to BigFat for a while it will reach serveMax and only serve ads from other batches.
The backends could have a report period of seconds, minutes or even hours depending on serveMax. A brand new batch with millions of impressions left can run safely for a long while before reporting back.
When BigFat gets a call to deductAndGetNextRateAndMax it deducts the number of served ads and then returns something like 75% of the total remaining impressions up to a configured max. This means that at the end of batch, if it isn't refilled, there will be some ads delivered after the batch is empty but it's better that the batch is actually depleted rather than almost depleted for a long time.

Storing 'debits' and 'credits' to maintain a 'transactions' table

Which is the better schema for a transactions table:
customer_id
type (enum, 'Debit' or 'Credit')
amount (decimal, unsigned)
or
customer_id
amount (decimal, signed... negative numbers are debits, positive numbers are credits)
The second solution seems simpler, but I'm worried I'm missing some potential pitfalls.
The second one is easier, and more efficient. It becomes much easier to query in the future, specifically for balances.
Background
Debits represent things you own, and credits represent things others own. They aren't the same dimensional units, and shouldn't be stored in the same database column. Using the sign bit to represent debits or credits is an oversimplification of how double-entry bookkeeping works; this oversimplification keeps popping up in low-end and homebrew accounting packages though, probably because it's the way laypeople think about accounting. [1]
The easiest way I've found to bring software devs up to speed with double-entry bookkeeping is to note that a number in an accounting system isn't a scalar -- it's a vector. The vector elements consist of a dimensional axis (debit or credit) as well as a magnitude (a signed fixed-place decimal). [2]
Solutions
Your first solution represents the vector nature of the data, and follows generally accepted accounting practices, but still stores the magnitude element in the same column, regardless of which axis it applies to. This makes SELECT statements more complicated.
It would be better to split the debit and credit magnitudes into separate columns; this gets rid of the need for the axis (enum) column, simplifies the SQL, is probably a performance improvement, and is the more conventional approach.
Your second solution (overloading the sign bit to represent debits or credits) scares me every time I see it, because I can never be sure if the architect is somehow compensating elsewhere for the lost dimensional information, or just didn't understand the vector nature of accounting data. Judging by what I see on SO, there are apparently a lot of accounting packages written that way, but it makes for complex, fragile, slower code and data structures, all in the interests of saving a tiny fraction of database space.
Source
Once upon a time, I was a trading systems engineer for an international bank. Corner cases bad, simple code good.
Footnotes
[1]: I think folks stumble into thinking "negative values are debits" in part because of the way banking works; banks use language which gives people the wrong impression of what a debit is. From the perspective of the bank, your checking account is something someone else owns -- it has a credit balance. When you deposit money in the bank, they tell you they are "crediting" your account, and when you withdraw, they say "debiting". But that's all from the bank's perspective, so it's all backwards. In your own accounting system (if you had one), debiting your checking account means increasing the balance, and a credit is a decrease. From your perspective, that account has a debit balance, because it's something you own.
[2]: If your language supports fixed-decimal-place complex numbers, they might be a handy way to manipulate accounting data; debits might be on the real axis and credits might be imaginary. This creates some interesting properties; a balanced set of entries would have a phase angle of 45 degrees, and so on. But unless your DB engine supports complex numbers natively, you'd wind up splitting the real and complex components into two columns for storage anyway, and those columns would be called "debits" and "credits".
I worked with an accounting system used by some big companies. The general ledger transaction table had separate columns for debits and credits. Something like:
customer_id
DebitAmount (decimal)
CreditAmount (decimal)
Only one of the columns would have a value greater than 0, the other was always 0. It doesn't seem very efficient, but it worked. Basically, you have to pick a convention and make your code work with it.
Isn't it generally better to have:
entry_id // PK
date
amount // always positive
debit_account_id // FK to accounts table
credit_account_id // FK to accounts table, not equal to debit_account_id
This way you always have matching double entry bookkeeping?
Some accounts will be customer accounts, one will be the accounts receivable account, etc.
See also this chapter from Martin Fowler's Analysis Patterns book.
The second may be easier BUT if your system becomes more complicated such as needing to track types of debits and/or credits then you may want have a type field. In classic accounting with T-Accounts you have to have matching debit and credit transaction types.
http://www.ehow.com/how_5130376_balance-taccount.html
http://www.cliffsnotes.com/WileyCDA/CliffsReviewTopic/T-Accounts.topicArticleId-21081,articleId-21009.html
I did this in a system once and had a type if you will. Each type represented a right or left side transaction.
Definitely made for harder code but it was a requirement of the system and worked out great.
Storing debits as negative numbers is a bad idea. In double-entry accounting, a debit to a debit-type account, like AR, is an increase in the AR balance. Conversely, a debit to a credit-type account, like Income, is a decrease in that account's balance. I too have worked with a system where every transaction record had a debit amount field and a credit amount field. I, too, found that wasteful. For our internal billing application, I use transactions that specify a debit account and a credit account, by id. There is another table, a Chart of Accounts, that stores all our accounts. Each account has a type of either debit or credit.
To get a balance on a specific account, the amount is positive if you are debiting a debit account, otherwise you negate the amount.
We have two tables:
ChartOfAccounts
**id name code Type**
------------------------------
1 AR 100 debit
2 Cash 200 debit
3 Income 300 credit
**Transactions**
**id date debitAcctId creditAcctId amount**
------------------------------------------------
1 9/16/15 1 3 100
This is a charge. We debit AR and credit Income
2 9/20/15 2 1 50
This is a payment. We debit Cash and credit AR.
Transaction 1
We debit AR to increase its balance. We credit Income, a credit type account, to increase its balance. AR tells us what we've owed. Income tells us what we've "earned". Our transaction follows the basic accounting principal of debiting one account and crediting another. Debit AR, Credit Income. In this case, both those account balances were increased.
Transaction 2
When we receive a payment, we need to remove that amount from AR, because we are no longer owed it. To remove money from AR, a debit account, we credit the amount to AR. Payment is considered cash, so we want to add the amount to our Cash account. Cash is a debit account, so we debit the payment amount. Again, we obeyed basic accounting by having one debit and one credit: we debit Cash and credit AR.
Now we want to find our AR balance. We want to find all debit and credit transactions to that account.
Since transactions store the account id, we need to know that the id for AR is 1. We find transactions that have AR's id of 1 in either their debitAcct or creditAcct fields.
Then we must know that AR is a debit type account. The amount of any transaction that debits AR is considered a positive amount. The amount of any transaction that credits AR is considered a negative amount.
Because we are finding transactions that either debit or credit AR, we can check if AR is the debit or the credit account. If the debit, then we leave the positive amount. Otherwise, we negate the amount.
-- find our AR balance from all transactions
-- debiting or crediting our AR account
SELECT
-- if our transactions debit account is AR, which is a debit-type account,
-- our amount should be positive, otherwise we make our amount negative.
IF(Transactions.debitAcctId = 1,amount,-amount) as amount
FROM
Transactions
WHERE
Transactions.debitAcctId = 1
OR
Transactions.creditAcctId = 1
The above sql is not meant as a working example, just the general approach.
The gist of what I'm saying is that you should treat transactions as general ledger entries that debit one account and credit another. If you have accounting actions that need to debit/credit more than two accounts, create more transactions.
Treat the amount of the transaction as positive or negative depending on the type of account involved.
Using the above methodology, I have built single sql statements that efficiently show the balance for multiple accounts at one time.
The second schema doesn't support basic accounting principles, as stated. The first schema would be one way to store the data, the other that makes sense would be to have two seperate columns for credit and debit.
What I really would liked to comment is the answer from Doug McClean. You can have only debit and credit one account, that would enforce a correct balance for sure. However, in reality, there is often more than one account involved, e.g. for taxes. So that model doesn't capture accounting well.
The 2nd solution is simpler & more efficient & readable
The 1st one will just add more complication to queries when you want to do some aggregation (sum, avg,...) as you have to translate it to a 'sign'.
An enum column would be of use when there is more categories and/or categories that can not be distinguished by the value only: DEBIT/CREDIT/TAX/...
I recommend that Debits and credits may have to remain as separate columns with 'unsigned' attribute.
Resultant calculated Balance column (i.e. cumulative debits summation- cumulative credits summation columns) will have to show a negative value with minus sign(-) when debits exceed credits.
When it comes to working out a query to get trial balance on a given date, this should be zero as per the philosophy of doub.entry.bk.keeping. Using your method the total becomes double because all the balances are deemed to be positive numbers.
In summary, some important queries become more difficult to write if you structure your data this way.
In fact, the question was asked quite a while ago, but in search of my answer in 2022, this article came across to me first on Google, so I will write my solution here.
I would just put it in the type table
transaction_type
id name operationType(true|false)
transaction
id amount type_id ...
And about the fact that balances may not coincide with transactions. I don't know how it was in 2009, but now we can use internal transactions in the database to make sure that all queries are completed, otherwise roll everything back, and also freeze the balance field of the user. This ensures that parallel queries cannot be executed. They will wait until the transaction is completed and the balance field is unfrozen