March 1, 2016
In today’s show, we discuss background or batch processing which is used when performing large database upgrades, migrating data, changing data structure, or anything that requires a lot of API calls or database queries. You want to do this in a way that doesn’t crash your server.
There are a lot of different definitions of batch and background processing. Pippin recently introduced a batch process reset tool to Easy Digital Downloads that resets and calculates earnings and sales records. This process is a site administration user action, and it displays a progress bar as it proceeds. If there are 1000 records, they do 10 at a time, so there are 100 batches.
A good example of background processing is SearchWP by Jonathan Christopher. When the plugin is installed it creates a database that is an index of everything on the site. The plugin shows a progress bar of the indexing process, but if you close it the plugin continues indexing or processing in the background.
WP Offload S3 goes through content and updates URLs of media files to the S3 URLs. If there are thousands of posts, you want this to happen in the background. It has to be efficient and reliable and not use too much memory. In order to conserve memory, it does one process at a time then starts the next one. This is a background processing system.
In today’s episode we talk about:
- How WordPress 4.3 uses cron to run a term splitting background process
- Distinctions between scheduled tasks and background processes
- The difference between a true cron job and running an event through wp-cron
- What are the characteristics of a good background processing system
- WP Offload S3 uses a chaining process to conserve memory use
- How WordPress would be better served if there was a centralized background system built into the core
- Building in fail safes for critical background processes such as payment processing
Resources mentioned in this podcast:
- WP Offload S3
- Techcrunch WP Async Task
- Background Processing in WordPress by Ashley
- WordCamp Atlanta 2016
If anybody wants to get started with background processing, the library Ashley wrote is a good place to start. If you have any questions, let us know. We are also excited that Apply Filters is a sponsor of WordCamp Atlanta 2016.
If you’re enjoying the show we sure would appreciate a Review in iTunes. Thanks!
INTRO: Welcome to Apply Filters, the podcast all about WordPress development. Now here’s your hosts, Pippin Williamson and Brad Touesnard.
PIPPIN: Welcome to Apply Filters, Episode 56. Today, Brad and I are going to discuss background processing. Background or batch processing is something that a lot of times you’ll use if you need to perform a large database upgrade, migrate some data, change some data structure, or do anything that requires a lot of API calls or queries to the database, et cetera, and you need to do it in a way that is hopefully not going to kill a server or prevent a website from running.
Brad, maybe jump in here and see if we can have a basic discussion of what is batch and background processing. I think we should first put out that there are a lot of different definitions of it. There are a lot of things that kind of fit, maybe others that don’t. And so there’s no, maybe, rigid definition of what this is.
BRAD: Yeah. I think we could probably start off by kind of just visualizing a process that you might run. Don’t you have a process in Easy Digital Downloads that you guys did recently where you updated a bunch of user accounts or something – wasn’t it?
PIPPIN: We do. We actually have a couple. One that we did recently is we introduced a recount tool that allows you to go in and either reset or recalculate earnings and sales stats for products and customers. One of them goes through and reconnects purchase records, if they’re missing, or if stats are incorrect, for all of your customers.
BRAD: Right. Does that show a progress bar or anything like that?
PIPPIN: Yes. This one is a user or site administrator initiated action, and then it will display a progress bar as it proceeds. This is maybe a little bit more of what we might call a batch process as opposed to background processing because it’s something that it shows a status. I think this is maybe an important distinction for us to make.
BRAD: For that to run, do I have to keep that page open and let it run?
BRAD: Okay, so basically you’re just using ajax calls or something to do ten records at a time or something like that, right?
PIPPIN: Yep. Yep, that’s exactly it. Let’s say we have 1,000 records. We do 10 at a time, so we have 100 batches.
BRAD: Right. That’s a good example of batch processing, but it’s not background processing.
PIPPIN: Correct. Right.
BRAD: Because, if I close that thing down, it shuts down the process, and you have to start it up again.
PIPPIN: There’s a good example of background processing in the WordPress world. If you’ve ever used SearchWP from Jonathan Christopher, he has a really excellent background processor in there. When you install his plugin, it creates a database that has an index of all of the content on your site. It can show a progress bar that shows how it’s proceeding. But, if you close it, it continues on its own, and so it doesn’t matter whether you’re on the page or if you’re even on your website. That process continues to run in the background, and so that would be a really good example of a background process.
BRAD: Right. That’s an excellent example, and I really love the way he shows the progress of that background process. The first time I saw that, I think it was the first time I’d ever seen a background process in WordPress with a UI displaying the progress as well.
PIPPIN: If it wasn’t the first one I’ve seen, it was the best one I’ve seen.
BRAD: Yeah, exactly.
PIPPIN: Yeah, it’s really well done. You should definitely check it out if you haven’t.
PIPPIN: And the plugin itself is just stellar.
BRAD: Yeah. Yeah, we use it. Yeah, I think that pretty much takes it, doesn’t it? That pretty well defines what batch processing is versus background processing.
PIPPIN: Mm-hmm. I think so. Maybe this will continue our discussion a bit, but do we have a good example of where WordPress Core uses background processing?
BRAD: Yes, and it’s a recent example in WordPress 4.3. WordPress 4.3 had term splitting, so I won’t get into it. We talked about the taxonomy roadmap on the show before and splitting terms and all that stuff. In 4.3, they have a background process that goes through, finds any shared terms, and then splits them. That thing uses wp-cron, and it’s got a locking mechanism so that it won’t queue up a bunch of background processes.
PIPPIN: That’s a background process introduced in 4.3. Let’s say that you have 10,000 terms in your database and 5,000 of them are shared. It’s going to go through and do ten terms at a time, and this process will continue to run in the background until all the shared terms are split. Is that correct?
BRAD: That’s correct. That would be 500 times in that case, so it could take quite a long time for it to run, especially if it’s a low traffic site that hasn’t been configured to run cron every minute or every five minutes.
PIPPIN: If this is using cron, what’s the difference between, say — maybe we should have a quick discussion on this. A cron job is not exactly a background process. It is in a way in terms that it’s background, so maybe what’s the relation or different distinction between a cron job that just happens to run behind the scenes and this particular background processing?
BRAD: Yeah. We were talking about this before the show. I’m starting to think that, well, the original position I took was that, well, it’s a scheduled task. It’s not really a background process because a background process needs to kind of run at length, and it’s kind of a one-time thing. But really, it’s a process that runs in the background, still.
PIPPIN: Maybe a distinction that we could make is that background processing, at least in the realm of, say, upgrading databases or performing a lot of queries, is kind of like a one-time thing in that we have a set of data, and we need to perform some kind of manipulation on it. Once we’ve gone through all the data, we’re done, and we don’t need to run this again. It’s not something that’s going to run every single day forever. It’s going to run until it’s completed, and then we’re done.
BRAD: Yeah. I guess someone might argue also, though, that if you schedule a post in WordPress that it’s just going to run that one time to publish that post, so it kind of is like a background process too, so I don’t know.
PIPPIN: Yeah. There’s definitely some overlap.
BRAD: Yeah, I don’t know if there is a distinction or not. The background processing in core that’s in there now for term splitting, it’s kind of interesting the way they did it. It does a batch of ten; it looks for ten shared terms. If it doesn’t find any, then it just sets some flags that it’s already done the term splitting, and then it cleans up a couple of things that it stuck into the options table, so it cleans that up.
But, if it does find some shared terms, it’ll split those, and then it’ll schedule wp-cron to run again in two minutes. In your example, you said 5,000 shared terms. Doing 10 at a time, so that’s 500 times it would need to run. That’s 1,000 minutes. I don’t know how many hours that is, but that’s quite a few. That’s only if wp-cron is configured to run every two minutes. If wp-cron isn’t set up to run every two minutes, it could take even longer than that.
PIPPIN: Yeah, so on an active site that has consistent traffic throughout the day, it could take up to 16, 24 hours if you have approximately that many terms.
BRAD: Right. Which, for this, for this particular example is perfectly fine because splitting terms is not high on people’s priorities list.
PIPPIN: Right, it’s not something that has to happen immediately.
PIPPIN: This uses the wp-cron system. I think most WordPress developers know that wp-cron is notoriously finicky, and not necessarily for any fault of wp-cron. It’s just because of the way the system works and, because of a huge number of less than stellar hosting systems out there, it can be a little bit unreliable. Do you think there are disadvantages to relying on wp-cron for a background process like this?
BRAD: Yeah, I think there is. Maybe you should explain. Do you want to quickly explain the problems with wp-cron and basically how it works, like in its default state, and then how you can configure it to maybe be a bit more dependable?
PIPPIN: Yep. Okay, so let’s first cover really quickly how cron itself works on a server, normally. Cron is a scheduled task system so that, let’s say, you want to run an action every single hour. What happens is the server knows that you want to run that task, and it executes the task from the server. Many times that’s simply calling a URL. It executes it, and it’s initiated by the server itself, which is very, very important. That’s how standard cron works.
Wp-cron doesn’t have the ability to or there are limitations on setting up a true cron job on a server, so it has a kind of roundabout way of doing it. What it has is, basically, when you schedule a cron event through wp-cron, it stores that event in the database with a time stamp. It says this is the time that it should execute. Then, every single time your site is loaded by a visitor, WordPress goes and looks to see, "Do I have any scheduled events that need to occur?" It queries the database and says, "Okay, I have a scheduled event that needs to happen right now. Let’s run it."
BRAD: Right. Except if you have a low traffic site.
PIPPIN: Right, and so here’s where the problem comes in. Let’s say, for example, that you schedule an event at 5:30 p.m. tomorrow. However, your site is low traffic, so the first visitor that loads your site at 7:30 p.m. the next day, because there was no traffic to trigger the cron to run at 5:30, the cron job gets missed. What happens then is, at 7:30, WordPress says, "Oh, look. I missed this event. Let’s go ahead and fire it. It’s better to run it two hours late than to not run it at all."
This is the reason that scheduled posts in WordPress are notoriously unreliable for scheduling exactly when you want them if you have a low traffic site. If you have a high traffic site, it works pretty well because, if you have people loading your site every minute or every couple of minutes, most of the time you can rely on wp-cron pretty well for things like that.
One of the ways that people fix wp-cron is they set up a true cron job on their server that fires an actual request to the wp-cron.php file every five minutes, every ten minutes, every minute, or something like that, so it simulates a request to load the site, which then fires off the scheduled event system in WordPress.
BRAD: Yeah, and that usually works pretty well, right?
BRAD: Yeah. There’s another problem with wp-cron in its default state, so without setting the server cron job. That is if you have a really high traffic site. Let’s say a cron needs to run, and three people hit it at exactly the same time. That cron will run three times because of the locking mechanism. If three people hit it at exactly the same time, the lock hasn’t been set for those three requests, and so it actually will fire the cron three times. That’s a huge problem on high traffic sites as well.
A huge problem for your server too if you have three processes running that really should only be one running. Yeah, I think that might be a problem here as well because the term splitting is using a wp-cron. They do have a locking mechanism, but I don’t see how a race condition would be avoided. I took a quick look at the code. It looks to me like it could end up spawning three instances. If three people hit at the same time, it could try to split the same terms three times, but I think that would probably be okay.
PIPPIN: Yeah. I think that would be okay, and I would guess that, in terms of the decision for how the term splitting was done, is that it’s a minimal enough risk that it’s okay. It’s something to be very aware of if you were relying on wp-cron or similar to perform very, very important actions. One example of that would be if you are processing payments through cron. If you need to initiate a charge or something like that through an API, be very careful about using cron or something like that because of the possibility that you might create duplicates or run into a race condition because of high traffic. Race conditions are a whole lot of fun.
BRAD: Oh, yeah. I just can’t wait until the next one. First of all, we’re kind of poking holes in WordPress 4.3 and their decision to use wp-cron for this background processing, but I want to be clear. Those guys, dealing with term splitting and all the taxonomy stuff, and fixing that up, the last thing they needed on their plate was to design a background processing system for WordPress Core. Obviously this works, what they’ve come up with, and that’s all that matters. Yeah.
PIPPIN: I want to be clear as well. I think they did an excellent job with it. But I do think it is important that we do recognize that there are potential problems with it, mostly if we’re going to use it as a model for how to do big and reliable background processing.
BRAD: Right. Right. Yeah, exactly. A lot of people look at WordPress Core as a model for doing things a certain way. Yeah, I would say that that is not necessarily a good way to do background processing for every situation. In fact, I’m pretty confident in saying it is not a good way for every situation. It worked in this case, but for other cases it would be bad.
PIPPIN: Why don’t we use this as a point to go ahead and diverge a little bit and kind of discuss what would a good background processing system look like? What are the characteristics of it? How do you do it, et cetera?
I know this is something that you guys have done a lot recently with your work in WP Offload S3. Why don’t we go through quickly and look at kind of how you guys did it, how it works, and then also maybe consider let’s maybe quickly discuss what it would look like if a standard system was built into WordPress core that would allow other plugins to extend it and to utilize background processing in core instead of rolling their own? Maybe first we should define, like, look at what is a good background process.
BRAD: The reason we need it is for our plugin, WP Offload S3. It has to go through all of your content and update the URLs to your media files, to replace them with the URL to the file on S3 or CloudFront or some other CDM. Basically, we’re doing a find and replace on all your content. And, if you have thousands of posts, which lots of people do, you don’t really want to be sitting there watching the screen and waiting for that to happen. You want that to happen in the background so that you can continue with your work.
I actually wrote a blog post recently and actually put out a library that’s pretty use to reuse in your own projects to do background processing in your WordPress plugins and themes. It’s inspired by Laravel, and other PHP frameworks, and the way they do background processing. Also, TechCrunch has a library called WP Asynchronous Tasks. Some of the inspiration came from that as well.
The goals for the library, or for the library that we’re using in WP Offload S3, is for it to be efficient and reliable, but it has to be compatible with WordPress’s minimum system requirements, so like it can’t exceed the minimum memory. I think it’s like 40 megs of memory. It has to be able to work with very little system resources, but also be efficient and reliable, so it’s a bit of a challenge.
One of the big things is that there’s no batching, so we’re not doing batch processing. What we do is we look at how much memory we can use, and we use that much. We set the limit of the process, execution time. Most Web servers are configured for at least 30 seconds, so they’ll process PHP for 30 seconds before killing it. Just to be safe, we’ve set the limit to 20 seconds, so this thing will run for 20 seconds and use as much memory as it can, and then it will fire off an asynchronous request to run the next one right away.
It’s kind of like a chain. It’s like run, run, run, and then fire off the next request, and then stop that one, and then the next one kind of picks up where the other one left off. And so it’s one process at a time, but it’s this kind of chain thing. If this chain gets disrupted, like say someone decides to reboot the server while this chain is happening, it’ll stop because it relies on the process finishing and then firing off the next one.
PIPPIN: What if you needed to resume a process in that case? Is there a way to handle that in what you’ve built?
BRAD: Yeah, we’ve built a wp-cron health check, basically. I don’t know what it’s set to, but every so often it just checks to see if the process is kind of hanging. If it is, then it fires it back up.
They have something in Linux. I can’t remember what it was called, though. It’s a similar thing because Nginx or Apache or whatever, these processes, they crash too, right? Sometimes they die, and so there’s this daemon process that just kind of monitors these other processes to see if they die. If they do die, it boots them right back up, so it’s a similar kind of thing. Yeah. That’s kind of how our system works, and it’s been working really well for us so far.
PIPPIN: Do you have any data on what maybe the largest amount of data that you’ve processed is through it?
BRAD: I don’t think so. No, I don’t have any idea of that. No, I’d just be guessing. I know some people that have used our plugin have pretty huge sites. For example, Bloomberg is one of our customers.
PIPPIN: A pretty large site.
BRAD: Yeah, they have a few properties. The guy says, "We have a few different multisite installs that we’re using it on." I’m like, "Okay," because that’s a massive media company. I can’t imagine how much.
PIPPIN: It’s got to be massive.
BRAD: How big their libraries are and stuff, it must be huge. Yeah.
PIPPIN: Looking at that library that Ashley wrote, it’s very reusable, for sure. Do you think that that is a good candidate for what something perhaps in core could potentially look like?
BRAD: Yeah. I absolutely do. I think we could definitely propose this. I don’t know how to go about it, necessarily, though, because this is kind of something that doesn’t really work well as a feature plugin. Really, what we need is we need plugin developers to use this. We need them to be using it and them to try it, so basically integrating it into their products. It’s better served, I think, as a library.
I mean I guess, if we made it a plugin, people could require the plugin, but it’s kind of clunky, right, that when you install your plugin, you also have to install this other plugin. I think that confuses people sometimes. They’re like, "Why? Why do I need to install this other plugin?"
PIPPIN: I may be wrong. Please correct me if I am. It looks like the library, WP Background Processing, that Ashley wrote kind of mimics the Heartbeat API I WordPress.
BRAD: It’s the chaining part of it doesn’t exist in the Heartbeat API.
PIPPIN: Gotcha. Similar, but definitely a little bit different, especially when you think of it in the chaining context. I think that is definitely an important distinction.
Do you think that there are concerns, maybe–and this was a big concern that people had when the Heartbeat API was introduced–with developers being negligent with it and causing performance problems in sites?
BRAD: Oh, yeah.
PIPPIN: Is that potentially a big enough concern as a reason to not do it, or is that just one of those, "Look. We can’t always protect against negligence"?
PIPPIN: That doesn’t mean that we should stop building our features.
BRAD: The last one. I’ll go with the last one. I think there’s a million and one ways to break WordPress. Any irresponsible plugin can break WordPress in so many different ways. To hold back progress because it might be too dangerous, I guess there are probably certain things we probably shouldn’t do because they leave that door open just a bit too much. But, in this case, I don’t think that’s the case. I think this would be a lot better served if it was in WordPress Core, and here’s why.
Let’s say my plugin implements background processing, which it does, and yours does too, and WooCommerce does or whatever. Let’s say both of your plugins do: Affiliate WP, EDD, and my plugin. I’ve got these three plugins installed on my site. WP Offload S3 fires a background process, starts a background process, and then yours does as well, and both of yours do, EDD and Affiliate WP. Now there are three background processes running at the same time. That’s not good, right?
Depending on how it’s been implemented, if my plugin is in watching memory, you could end up using up too much of the resources, slowing down the site, and causing problems. Whereas, if this was a system that was a centralized system built into WordPress Core, and I kicked off a background process from WP Offload S3 and you kicked one off from EDD, and Affiliate WP did the same all at once, it would be put in a queue, and they would go one after the other, if that made the most sense.
PIPPIN: Yeah. I think that’s an excellent point where WordPress Core could control the queue from all plugins and make sure that everything is running properly and not killing the server. Whereas, if you have three independently, well, EDD doesn’t know about S3. Affiliate WP doesn’t know about — they could potentially know about each other. But, because everybody builds things differently, you probably do have a higher risk of conflicts or problems.
BRAD: Yeah. I think the current state of affairs is bad because the more plugins that do background processing, the more are going to compete, going to be kind of overlapping in their processing, and causing problems stepping on each other’s toes. I would love to see this worked on to get into WordPress Core.
PIPPIN: Do you think this is something that you might, at some point, be interested in spearheading in getting it into core?
BRAD: Yeah. I think we’ve done a lot of the legwork already. I think this is going to be something that’s going to be very difficult to get into WordPress Core because, to make it reliable, I don’t think there is 100% reliability here. I think it’s going to have to be a kind of "good enough" scenario, but where is that point? What is good enough to make it into WordPress Core?
I do think it’s going to have to be iterated upon as well. The system that we’ve built is not flawless by any means, so there’s going to have to be refinement goring on there. I think it’s going to be difficult to do that, though. How could you approach that? I just mentioned the feature plugin thing and how that’s not going to really work very well.
PIPPIN: Well, you know, I think that’s the same argument that a lot of people are having with the REST API in core, and this is a rabbit hole that we should probably maybe avoid. It’s basically the argument of, well, do we want to get it perfect before we put it in, or do we need people to start using it and then we can iterate on it? To get people to use it, one of the only ways to do that is to put it into core.
BRAD: Yeah. Yeah.
PIPPIN: I think that’s a constant battle, and I’m not even going to say anything about one side or the other because everybody has valid points. But, any time you’re discussing a feature, and it doesn’t matter what kind of feature it is, you have that discussion. Always, the easiest way to test a feature is to, well, give it to everybody and see what happens.
PIPPIN: It doesn’t necessarily mean it’s the smoothest process.
BRAD: That’s a good point, the stuff that you just brought up. I think if we just took what we have built, adapted it to WordPress Core, put it into WordPress Core tomorrow, allowed people to start using it, and then refined it that way, that’s probably not that bad. What better way to refine it than to get people using it, right?
PIPPIN: To play devil’s advocate for a moment, the big reason not to is, let’s say that you put it and then you realized you have to change it drastically for one reason or another, but you already have 20 million websites running it.
BRAD: Hmm. Well, I mean–
PIPPIN: That is the problem with scale.
BRAD: Yeah. Well, you wouldn’t necessarily. You would have the 20 million websites that are using a certain plugin that’s using it.
PIPPIN: Well, right.
BRAD: Whatever subset that would be.
PIPPIN: Right. Obviously whatever the numbers are, they’re going to always be different, but it’s still kind of the premise of: Tere is a problem when you have such a large scale. The idea of beta testing life is a little bit more dangerous.
BRAD: Yeah. I think a good parallel to this situation is the plugin requirements or plugin dependencies. You’re installing a theme and a theme requires certain plugins, so it let’s you know. You can click a button, and it downloads.
There’s a library out there, TGM Plugin Activation, I think it’s called. It was originally written by Thomas Griffin. TGM stands for Thomas Griffin Media. Do you know about that? Have you looked at that library?
PIPPIN: I have. I haven’t personally used it in any of my plugins, but I’ve seen it used many times.
BRAD: Yeah. I think that’s a similar situation because that, I think, would also be a good addition to WordPress Core, the ability to have plugins require other plugins or themes require certain plugins.
PIPPIN: I know it’s something that a lot of people have been wanting.
BRAD: But it’s a similar situation in that, like, what’s the process to get that into WordPress Core? How do you get that out there and test it? It’s a difficult one.
PIPPIN: This is why we have track tickets that sit for years and years and years.
BRAD: Yeah, that’s right. Yeah.
PIPPIN: Do you think, maybe as a wrap-up for people that are interested in implementing background processes in their own plugins–I think we’ve given them some resources to go off of, some things to think about–what are perhaps some quick examples of background processes you should avoid or things you should not do in a background process? Do you have any quick examples?
BRAD: I’m struggling here, off the top of my head. Yeah. Do you?
PIPPIN: The main one that I can think of, at least one that you should implement with extreme caution, is payment processing. My only reason for saying that is you have to remember that let’s say you’re building an e-commerce system, invoicing, or anything, whether it’s a plugin or you’re doing it for your own clients, or something like that. It’s important to remember the vast differences there are with posting infrastructures within WordPress and within any other kind of system that runs on the Web.
If you are using any kind of process, like a batch process, to process payments or things like that, things that are very, very important, you have to be ready to account for failure and make sure that you’ve implemented those correctly. One of the advantages that giant merchant processors like Stripe and PayPal have is, well, billions and billions of dollars to build systems that don’t fail or that have good fail-safes. Your $7 WordPress host doesn’t have those. I think this applies to anything that you’re doing with not just background processing, but really anything, in general. Just be cognizant of the idea of failure and how you handle that.
BRAD: Right. How are you guys doing that? You guys just released a recurring payments add-on for EDD. You’re not doing background processing of payments in that?
PIPPIN: We’re not at the moment. We will be in the near future because there are certain payment processors that actually require it. Amazon Payments, for example.
BRAD: Right. Oh, I think we discussed this a little bit on the last episode where we discussed that you’re letting Stripe and PayPal and whoever do–
PIPPIN: Right. They handle the process and they simply tell us when it’s processed.
BRAD: Right. Right.
BRAD: Okay. But, like you said, there are some processors that don’t.
BRAD: That don’t do that.
PIPPIN: Yep, like Amazon.
BRAD: So you’re going to have to.
PIPPIN: Yeah. With background processing, I would just make sure that if you’re doing something that is very important or touchy, such as payments, just make sure that you’ve accounted for when it fails, or when you have a race condition, or things like that. You don’t want to suddenly find out that you failed to collect $20,000 in payments from customers for the last month, that somebody was charged 5 times because there was a race condition, or things like that.
BRAD: Yeah. I think the other thing to look out for is background processes that are called "zombie processes." They just kind of get detached from your system somehow, and they just go on forever or a really long time. That’s more common in an operating system environment where things can just kind of keep running.
In this case where we’re talking about Web requests, usually they just die. Eventually they’re going to die, unless you have your server configured to kind of run PHP forever, which I don’t think too many hosts do that. If you’re on a Web host, they’re going to kill it. If you have a process that’s been running for too long, they’ll kill it for you, so I guess it’s not that big a concern in this case.
Yeah. Should we wrap it up?
PIPPIN: Yeah. Background processing is fun stuff. I think, for anybody who wants to dive into it, probably one of the best places to start is the library that Ashley wrote. It’s excellent. And, if you have any questions, feel free to let us know. Submit them through Twitter, through the site, or find us elsewhere.
BRAD: I want to give a shout out to WordCamp Atlanta, who we are now a sponsor of, so that’s cool.
PIPPIN: Yeah. When is WordCamp Atlanta happening? It’s coming up pretty soon here.
BRAD: By "we," I mean Apply Filters is a sponsor of WordCamp Atlanta.
PIPPIN: Yeah. This is actually the first time this has happened, so this is kind of exciting.
PIPPIN: WordCamp Atlanta is March 18 and 20. Unfortunately, we will not, either one of us, be able to make it there, but we are happy to help out in any way that we can. If you’re there at WordCamp Atlanta, maybe let us know how it was.
PIPPIN: It looks like it’s going to be a great event.
BRAD: And enjoy.
PIPPIN: All right, everyone. Thanks so much for listening. Catch you next time.
BRAD: Talk to you later.