I'm in the process of making an app that will update shipping depending on what your order_count is using ShopifyAPI::Customer.
One main thing I'm trying to accomplish is being able to reset the order_count to zero for all customers.
I have been trying:
order = ShopifyAPI::Customer.all
order.update_all(:orders_count, "0")
It works when I'm addressing a single customer, but not Customer.all. Is there a way I can work around this to update all customers in the db at once?
Can you actually change a valid orders_count? It would be pretty crazy in my mind if Shopify allowed that.
If I were you, and I am not, for sure, I would simply change my algorithm so that it is more of a "How many orders has this customer placed since last tuesday?". That way, I reward them if their purchases exceed that threshold of X.
In your case, you are trying to erase their legitimate order history to make your counter work, which I doubt works.
In the meantime, if you can indeed set that count to zero, just loop through all the customers, and for each one, save their count as zero. There is no one shot all customers call in Shopify API, just like there is no one shot anything. Every resource is a one-off.
Related
I run a marketplace iOS app and from time to time we have "competitions", where we have an especially sought after item for sale for a good price, that drops as a specific time. Sometimes thousands of people will try to buy this item within 1-2 seconds and I therefore need to make sure that only 1 person will get the item. The solution I have for it now feels kind of clumsy, so I was wondering how a good solution would look like when I use Firebase as my database.
The process is as such:
User finds the item on his iOS app and clicks "Purchase".
A request is sent to our API (build on RoR) that processes the purchase (usually takes 10-20 seconds for the purchase to go through).
Right now, I set the buyers ID temporarily as an attribute on the item, I wait a second and check whether the buyer ID is still the same on the item. It works, but it doesn't feel optimal.
Any suggestions on how I can make sure 2 people can't purchase the same item?
To avoid something like this in your rails app, the keywords mutex and race condition should probably help you to find a bunch of appropriate gems.
I personally like to use redis for this kind of task, because in redis, transactions are atomic by default (https://en.wikipedia.org/wiki/Atomicity_(database_systems)).
So maybe this gem could suit your needs (untested): https://github.com/kenn/redis-mutex.
For the theory, refer to this articles:
https://en.wikipedia.org/wiki/Mutual_exclusion
https://en.wikipedia.org/wiki/Race_condition
Store in /items/foo
a record with the structure:
{id:<blah>, available: <timestamp>, (purchaser: null)}
let buyers write their user name to to buy:
/item/foo/purchaser
You want 3 things to happen.
Block someone writing before the servers timestamp of available
only allow 1 person to do the operation. Once the /item/foo/purchaser is set, you don't want it modifiable (i.e. write once)
only allow the authenticated user id to be used in the purchaser field
To enforce this logic you use security rules, on the subpath of "/items/$itemid/purchaser"
".write": "now > data.parent().child('available').val()" +// 1.
"&& data.val() == null" + // 2.
"&& newData.val() == auth.id" // 3.
My guess is tht you should use locks.
On a request coming in, check if you can acquire a lock. If yes, the the user is the first one. Then, the next requests won't be able to acquire the lock. This means the product as already been purchased.
Take a look at this redis doc part : http://redis.io/topics/distlock
At the application(RoR) level, you can set a flag(eg: lock_foo=true) that is shared across the cluster(can be in your cache store).
If this value is true, don't allow any other users to access the product/make the purchase.
You can definitely implement this with Firebase. As dvxam and Anshul Mengi mentioned, a lock system is the good way to go:
You could have on the document a property called lock:
{
"lock": {
"userId": "myUserId",
"expiresAt": "myTimestamp"
}
}
When a user clicks on the purchase button, you can use a Firebase transaction to make sure only one user can get the lock and that the first one gets it.
When another user clicks the purchase button, if a non-expired lock is present with a different userId, you can deny the purchase.
When the user completes the purchase you can then use another transaction to check if it is the same userId and if the lock is not expired.
Transactions are absolutely necessary here, and they are not available on the Firebase REST api (hence no more in the ruby wrapper), so you would need to run this code client-side using the iOS SDK, or to spin a nodeJS server for this task.
Hope it helps.
How about this for different:
When user clicks purchase, immediately create a purchase request record that contains product, user and timestamp, and then poll every few seconds to see if the purchase was successful
Run a background job that searches for un-purchased products that have at least one purchase request against them, and marks the product as purchased (selecting one purchase request / user as the "winner")
I'm not sure if there's a specific pattern I can apply in this case or how this is "normally" solved?
I can't speak to Firebase, but I can definitely speak to how this is "normally" solved in Rails and relational databases.
Before jumping in to code, note that it seems like you need linearizability, one of the hardest things to ask of a database, and some databases can't guarantee it even when they say they do. You might be able to hack around needing linearizability if all you need to know is whether it's been purchased or not, but I wouldn't take that hack lightly. Consistency in distributed systems is a really complex and edge-case-ridden topic, especially while under load (which it sounds like you'll be).
In Rails+RDB (postgres, mysql, sqlite) an atomic, linearized quantity update looks roughly like this (with some rails validation niceities thrown in):
class Product
validates :quantity, numericality: {greater_than: 0}, on: :purchase
def purchase
with_lock do # simultaneously aquires a lock and reloads the model
return false if !valid?(:purchase) # immediately release the lock if not valid
update_attribute(:quantity, quantity - 1) # saves without validation; YYMV
end
end
end
This general pattern of "lock+reload -> check -> update" is the gold standard for reliability, but it's "heavy." The first object to acquire the lock will win, but while it's doing its thing, all the other processes asking for a lock will be in queue. Somewhere there's a timeout and max connection pool defined, so if say 4000 locks are asked for within 1 second but it takes 10 seconds to determine success, you'll need 4000 connections and, even worse, the last lock asked for will be waiting for over 11 hours! That will make managing the connection pools and setting reasonable timeouts challenging.
The benefits, though, are that it will "just work" - if the first purchase fails, the next purchase will acquire a lock, and so on, until someone wins. Then, it will return helpful ActiveModel errors to everyone else in the queue. Additionally, it's simple enough code-wise that you know as long as your database provides linearizability, you're in the clear.
To mitigate the 11-hours issue hopefully you can very quickly deny everyone with outstanding locks to flush the queue.
I don't know exactly what you're doing while you try to make a purchase, but if it was just a credit card validation and a data update, I'd highly recommend the approach I've outlined with a database known to be linearizably consistent. Otherwise, you're going to need to consult a true distributed systems expert or run your users under the bus figuring this out.
Is there a way in Rails to send out a request at a certain time?
I'm using an external credit card charging API, and I want to adjust each monthly subscription based on how many referrals they have (10% each, 10 referrals max). The API has a beta referral system built in, but it doesn't seem to work the way I need it to. Plus, there are just too many unknowns that I'd rather not get into at the moment. I just want to get it up and working, and since my system is fairly simple, I'd rather just do it manually.
There's a billing date for each subscription, and what I want to do is just manually adjust the price of the subscription based on how many active users there are containing the referral code of the user being charged. I'd like to just send out this request to the API just before they're billed. Like sometime around subscription.next_billing_at - 1.minute.
Then just set the subscription.price to price - (price * (User.where(referral_code: current_user_code)).count / 10).
I'm aware this is far from an optimal approach, considering the amount of extra requests being made each month, but since we're small right now, it shouldn't be a problem. Again, it's just a temporary solution so we can get things running now.
There are two options which directly answer your question.
Write a rake task and run it daily with cron via the Whenever gem. If you take this approach, you will have to have the task just load all subscriptions which are due to be billed in the next cycle and update them as required.
Alternatively, use something like Resque-scheduler, which would allow you to run some task at next_billing_at - 1.minute or something.
But if you are small, why not just update the price every time a new referral is created using a callback? Unless there are specific rate or query limits on this API, I doubt a card processor is going to be affected by the traffic you generate. Of course if there are other requirements, like, a referral only applies after a month or something like that, you are going to be stuck with one of the first 2 options, and the Cron + Rake task is probably the best solution in that case.
I am using Rails 3.1.0 with Devise 2.1.0. I would like to limit the number of times a user can perform an action in a day. The main purpose of this limitation is to prevent spam.
I see many questions similar to this one but was wondering if there is a specific way to accomplish what I am trying to do through Devise.
For the actions that create model instances, the number of times an action has been performed in a day is easy to keep track of. However, at least one action that I would like to restrict does not create a model instance, so I'm not sure what to do about it.
I was also wondering if this is a legitimate/effective way of preventing spam (in addition to requiring users to register and sign in to perform the actions).
Personally, I find these sorts of systems to be over-complications. Unless spam is an existing, provable problem I'm not sure adding in a system that's likely to be rather extensive is a good use of time and energy.
Alternatives to this would be requiring registration through a third-party service (say Facebook) and using either captchas or exciting and new negative captchas.
That said, if you want to do this, I think the best place to keep track of it would be in an ephemeral data store. Redis would be really good for this since you can use queues. In the actions that you want to restrict, add a timestamp to the queue, and before you allow the user to perform said action, check the number of elements in the queue, purging ones that are too old while you do so.
That's sort of pseudo-codey, but should at least help you get started.
I'm currently developing an application where I need to implement some sort of way of handling credits which are purchased by users of my application. These credits will then be used to preform certain tasks inside my application such as purchasing hits in mechanical turk. The reason I need to do this is because in the case of mechanical turk there is a possibility that our orders won't be filled and instead of just keeping the extra money for hits they didn't get I want to credit them for future purchases.
The important parts I need help fleshing out is how do you accurately manage an ongoing total of credits. I can't obviously calculate it every time. Additionally I need to manage the adding and subtracting of the credits. Also I probably need to track the origin of these credits, ie money or free because it is possible we might give out free credits as a reward but we need to be careful how to handle turning free credits into cash because it opens an incentive for scammers to use the credits to purchase turk hits then do the turk hit themselves and keep the money.
I currently work on a system with something very similar.
Users have an Account which tracks all transactions - origin, type and amount. We have a finite number of transactions types (including obvious the ones like credit/debit). The Account table is therefore actually an archive of all of the transactions for a particular user account, rather than being a simple running total.
Account totals are therefore calculated every time we need them, although it would be pretty simple to add some aggregates to the database that increment/decrement a total value as transactions are processed. As with all performance-related issues - don't do it until you need to, summing values across some smartly indexed columns is fine for our current system.
I have a rails app that tracks membership cardholders, and needs to report on a cardholder's status. The status is defined - by business rule - as being either "in good standing," "in arrears," or "canceled," depending on whether the cardholder's most recent invoice has been paid.
Invoices are sent 30 days in advance, so a customer who has just been invoiced is still in good standing, one who is 20 days past the payment due date is in arrears, and a member who fails to pay his invoice more than 30 days after it is due would be canceled.
I'm looking for advice on whether it would be better to store the cardholder's current status as a field at the customer level (and deal with the potential update anomalies resulting from potential updates of invoice records without updating the corresponding cardholder's record), or whether it makes more sense to simply calculate the current cardholder status based on data in the database every time the status is requested (which could place a lot of load on the database and slow down the app).
Recommendations? Or other ideas I haven't thought of?
One important constraint: while it's unlikely that anyone will modify the database directly, there's always that possibility, so I need to try to put some safeguards in place to prevent the various database records from becoming out of sync with each other.
The storage of calculated data in your database is generally an optimisation. I would suggest that you calculate the value on every request and then monitor the performance of your application. If the fact that this data is not stored becomes an issue for you then is the time to refactor and store the value within the database.
Storing calculated values, particularly those that can affect multiple tables are generally a bad idea for the reasons that you have mentioned.
When/if you do refactor and store the value in the DB then you probably want a batch job that checks the value for data integrity on a regular basis.
The simplest approach would be to calculate the current cardholder status based on data in the database every time the status is requested. That way you have no duplication of data, and therefore no potential problems with the duplicates becoming out of step.
If, and only if, your measurements show that this calculation is causing a significant slowdown, then you can think about caching the value.
Recently I had similar decision to take and I decided to store status as a field in database. This is because I wanted to reduce sql queries and it looks simpler. I choose to do it that way because I will very often need to get this status and calculating it is (at least in my case) a bit complicated.
Possible problem with it is that it get out of sync, so I added some after_save and after_destroy to child model, to keep it synchronized. And of course if somebody would modify database in different way, it would make some problems.
You can write simple rake task that will check all statuses and, if needed, correct them. You can run it in cron so you don't have to worry about it.