diff options
| author | Mitja Felicijan <mitja.felicijan@gmail.com> | 2023-06-27 14:50:20 +0200 |
|---|---|---|
| committer | Mitja Felicijan <mitja.felicijan@gmail.com> | 2023-06-27 14:50:20 +0200 |
| commit | 8697555125c57ae64a0c9b78514b4aac4fd523de (patch) | |
| tree | a699df53a7c35a4425f30bca86982c4341f6de40 /content/posts/2017-04-17-what-i-ve-learned-developing-ad-server.md | |
| parent | 33b2615a5038bc85036081e8b5e0da8584d88097 (diff) | |
| download | mitjafelicijan.com-8697555125c57ae64a0c9b78514b4aac4fd523de.tar.gz | |
Massive formatting and added figcaption
Diffstat (limited to 'content/posts/2017-04-17-what-i-ve-learned-developing-ad-server.md')
| -rw-r--r-- | content/posts/2017-04-17-what-i-ve-learned-developing-ad-server.md | 154 |
1 files changed, 77 insertions, 77 deletions
diff --git a/content/posts/2017-04-17-what-i-ve-learned-developing-ad-server.md b/content/posts/2017-04-17-what-i-ve-learned-developing-ad-server.md index 1b9be06..bb98efd 100644 --- a/content/posts/2017-04-17-what-i-ve-learned-developing-ad-server.md +++ b/content/posts/2017-04-17-what-i-ve-learned-developing-ad-server.md | |||
| @@ -5,10 +5,10 @@ date: 2017-04-17T12:00:00+02:00 | |||
| 5 | draft: false | 5 | draft: false |
| 6 | --- | 6 | --- |
| 7 | 7 | ||
| 8 | For the past year and half I have been developing native advertising server | 8 | For the past year and half I have been developing native advertising server that |
| 9 | that contextually matches ads and displays them in different template forms | 9 | contextually matches ads and displays them in different template forms on |
| 10 | on variety of websites. This project grew from serving thousands of ads per | 10 | variety of websites. This project grew from serving thousands of ads per day to |
| 11 | day to millions. | 11 | millions. |
| 12 | 12 | ||
| 13 | The system is made from couple of core components: | 13 | The system is made from couple of core components: |
| 14 | 14 | ||
| @@ -16,56 +16,56 @@ The system is made from couple of core components: | |||
| 16 | - Utils - cronjobs and queue management tools, | 16 | - Utils - cronjobs and queue management tools, |
| 17 | - Dashboard UI. | 17 | - Dashboard UI. |
| 18 | 18 | ||
| 19 | Initial release was using [MongoDB](https://www.mongodb.com/) for full-text | 19 | Initial release was using [MongoDB](https://www.mongodb.com/) for full-text |
| 20 | search but was later replaced by [Elasticsearch](https://www.elastic.co/) | 20 | search but was later replaced by [Elasticsearch](https://www.elastic.co/) for |
| 21 | for better CPU utilization and better search performance. This provided us | 21 | better CPU utilization and better search performance. This provided us with many |
| 22 | with many amazing functionalities of [Elasticsearch](https://www.elastic.co/). | 22 | amazing functionalities of [Elasticsearch](https://www.elastic.co/). You should |
| 23 | You should check it out if you do any search related operations. | 23 | check it out if you do any search related operations. |
| 24 | 24 | ||
| 25 | Because the premise of the server is to provide native ad experience, they | 25 | Because the premise of the server is to provide native ad experience, they are |
| 26 | are rendered on the client side via simple templating engine. This ensures | 26 | rendered on the client side via simple templating engine. This ensures that ads |
| 27 | that ads can be displayed number of different ways based on the visual style | 27 | can be displayed number of different ways based on the visual style of the |
| 28 | of the page. And this makes JavaScript client library quite complex. | 28 | page. And this makes JavaScript client library quite complex. |
| 29 | 29 | ||
| 30 | So now that you know basic information about the product lets get into the | 30 | So now that you know basic information about the product lets get into the |
| 31 | lessons we learned. | 31 | lessons we learned. |
| 32 | 32 | ||
| 33 | ## Aggregate everything | 33 | ## Aggregate everything |
| 34 | 34 | ||
| 35 | After beta version was released everything (impressions, clicks, etc) was | 35 | After beta version was released everything (impressions, clicks, etc) was |
| 36 | written in nanosecond resolution in the database. At that time we were using | 36 | written in nanosecond resolution in the database. At that time we were using |
| 37 | [PostgreSQL](https://www.postgresql.org/) and database quickly grew way above | 37 | [PostgreSQL](https://www.postgresql.org/) and database quickly grew way above |
| 38 | 200GB in disk space. And that was problematic. Statistics took disturbingly | 38 | 200GB in disk space. And that was problematic. Statistics took disturbingly long |
| 39 | long time to aggregate. Also using indexes on stats table in database was no | 39 | time to aggregate. Also using indexes on stats table in database was no help |
| 40 | help after we reached 500 million datapoints. | 40 | after we reached 500 million datapoints. |
| 41 | 41 | ||
| 42 | > There is a marketing product information and there is real life experience. | 42 | > There is a marketing product information and there is real life experience. |
| 43 | And the tend to be quite the opposite. | 43 | And the tend to be quite the opposite. |
| 44 | 44 | ||
| 45 | This was the reason that now everything is aggregated on daily basis and this | 45 | This was the reason that now everything is aggregated on daily basis and this |
| 46 | data is then fed to Elastic in form of daily summary. With this we achieved we | 46 | data is then fed to Elastic in form of daily summary. With this we achieved we |
| 47 | can now track many more dimensions such as zone, channel and platform information. | 47 | can now track many more dimensions such as zone, channel and platform |
| 48 | And with this information we can now adapt occurrences of ads on specific | 48 | information. And with this information we can now adapt occurrences of ads on |
| 49 | places more precisely. | 49 | specific places more precisely. |
| 50 | 50 | ||
| 51 | We have also adapted [Redis](https://redis.io/) as a full-time citizen in our | 51 | We have also adapted [Redis](https://redis.io/) as a full-time citizen in our |
| 52 | stack. Because Redis also stores information on a local disk we have some sort | 52 | stack. Because Redis also stores information on a local disk we have some sort |
| 53 | of backup if server would accidentally suffer some failure. | 53 | of backup if server would accidentally suffer some failure. |
| 54 | 54 | ||
| 55 | All the real-time statistics for ad serving and redirecting is presented as | 55 | All the real-time statistics for ad serving and redirecting is presented as |
| 56 | counters in Redis instance and daily extracted and pushed to Elastic. | 56 | counters in Redis instance and daily extracted and pushed to Elastic. |
| 57 | 57 | ||
| 58 | ## Measure everything | 58 | ## Measure everything |
| 59 | 59 | ||
| 60 | The thing about software is that we really don't know how well it is performing | 60 | The thing about software is that we really don't know how well it is performing |
| 61 | under load until such load is presented. When testing locally everything is | 61 | under load until such load is presented. When testing locally everything is fine |
| 62 | fine but when on production things tend to fall apart. | 62 | but when on production things tend to fall apart. |
| 63 | 63 | ||
| 64 | As a solution for this we are measuring everything we can. Function execution | 64 | As a solution for this we are measuring everything we can. Function execution |
| 65 | time (by encapsulating functions with timers), server performance (cpu, memory, | 65 | time (by encapsulating functions with timers), server performance (cpu, memory, |
| 66 | disk, etc), Nginx and [uWSGI](https://uwsgi-docs.readthedocs.io/) performance. | 66 | disk, etc), Nginx and [uWSGI](https://uwsgi-docs.readthedocs.io/) performance. |
| 67 | We sacrifice a bit of performance for the sake of this information. And we | 67 | We sacrifice a bit of performance for the sake of this information. And we store |
| 68 | store all this information for later analysis. | 68 | all this information for later analysis. |
| 69 | 69 | ||
| 70 | **Example of function execution time** | 70 | **Example of function execution time** |
| 71 | 71 | ||
| @@ -99,27 +99,28 @@ store all this information for later analysis. | |||
| 99 | } | 99 | } |
| 100 | ``` | 100 | ``` |
| 101 | 101 | ||
| 102 | We have also started profiling with [cProfile](https://pymotw.com/2/profile/) | 102 | We have also started profiling with [cProfile](https://pymotw.com/2/profile/) |
| 103 | and then visualizing with [KCachegrind](http://kcachegrind.sourceforge.net/). | 103 | and then visualizing with [KCachegrind](http://kcachegrind.sourceforge.net/). |
| 104 | This provides much more detailed look into code execution. | 104 | This provides much more detailed look into code execution. |
| 105 | 105 | ||
| 106 | ## Cache control is your friend | 106 | ## Cache control is your friend |
| 107 | 107 | ||
| 108 | Because we use Javascript library for rendering ads we rely on this script | 108 | Because we use Javascript library for rendering ads we rely on this script |
| 109 | extensively and when in need we need to be able to change behavior of the | 109 | extensively and when in need we need to be able to change behavior of the script |
| 110 | script quickly. | 110 | quickly. |
| 111 | 111 | ||
| 112 | In our case we can not simply replace javascript url in html code. It usually | 112 | In our case we can not simply replace javascript url in html code. It usually |
| 113 | takes a day or two for the guys who maintain sites to change code or add | 113 | takes a day or two for the guys who maintain sites to change code or add |
| 114 | ?ver=xxx attribute. And this makes rapid deployment and testing very difficult | 114 | ?ver=xxx attribute. And this makes rapid deployment and testing very difficult |
| 115 | and time consuming. There is a limitation of how much you can test locally. | 115 | and time consuming. There is a limitation of how much you can test locally. |
| 116 | 116 | ||
| 117 | We are now in the process of integrating [Google Tag Manager](https://www.google.com/analytics/tag-manager/) | 117 | We are now in the process of integrating [Google Tag |
| 118 | but couple of websites are developed on ASP.net platform that have some | 118 | Manager](https://www.google.com/analytics/tag-manager/) but couple of websites |
| 119 | problems with tag manager. With a solution below we are certain that we are | 119 | are developed on ASP.net platform that have some problems with tag manager. With |
| 120 | serving latest version of the script. | 120 | a solution below we are certain that we are serving latest version of the |
| 121 | script. | ||
| 121 | 122 | ||
| 122 | And it only takes one mistake and users have the script cached and in case of | 123 | And it only takes one mistake and users have the script cached and in case of |
| 123 | caching it for 1 year you probably know where the problem is. | 124 | caching it for 1 year you probably know where the problem is. |
| 124 | 125 | ||
| 125 | ```nginx | 126 | ```nginx |
| @@ -143,10 +144,10 @@ location /static/ { | |||
| 143 | } | 144 | } |
| 144 | ``` | 145 | ``` |
| 145 | 146 | ||
| 146 | Also be careful when redirecting to url in your python code. We noticed that | 147 | Also be careful when redirecting to url in your python code. We noticed that if |
| 147 | if we didn't precisely setup cache control and expire headers in response we | 148 | we didn't precisely setup cache control and expire headers in response we didn't |
| 148 | didn't get the request on the server and therefore couldn't measure clicks. | 149 | get the request on the server and therefore couldn't measure clicks. So when |
| 149 | So when redirecting do as follows and there will be no problems. | 150 | redirecting do as follows and there will be no problems. |
| 150 | 151 | ||
| 151 | ```python | 152 | ```python |
| 152 | # python ➜ bottlepy web micro-framework | 153 | # python ➜ bottlepy web micro-framework |
| @@ -157,42 +158,41 @@ response.set_header("Location", url) | |||
| 157 | return response | 158 | return response |
| 158 | ``` | 159 | ``` |
| 159 | 160 | ||
| 160 | > Cache control in browsers is quite aggressive and you need to be precise | 161 | > Cache control in browsers is quite aggressive and you need to be precise to |
| 161 | to avoid future problems. We learned that lesson the hard way. | 162 | avoid future problems. We learned that lesson the hard way. |
| 162 | 163 | ||
| 163 | ## Learn NGINX | 164 | ## Learn NGINX |
| 164 | 165 | ||
| 165 | When deciding on a web server we went with Nginx as a reverse proxy for our | 166 | When deciding on a web server we went with Nginx as a reverse proxy for our |
| 166 | applications. We adapted micro-service oriented architecture early in the | 167 | applications. We adapted micro-service oriented architecture early in the |
| 167 | project to ensure when we scale we can easily add additional servers to our | 168 | project to ensure when we scale we can easily add additional servers to our |
| 168 | cluster. And Nginx was crucial to perform load balancing and static content | 169 | cluster. And Nginx was crucial to perform load balancing and static content |
| 169 | delivery. | 170 | delivery. |
| 170 | 171 | ||
| 171 | At first our config file was quite simple and later grew larger. After patching | 172 | At first our config file was quite simple and later grew larger. After patching |
| 172 | and adding new settings I sat down and learned more about the guts of Nginx. | 173 | and adding new settings I sat down and learned more about the guts of Nginx. |
| 173 | This proved to be very useful and we were able to squeeze much more out of our | 174 | This proved to be very useful and we were able to squeeze much more out of our |
| 174 | setup. So I advise you to take your time and read through the | 175 | setup. So I advise you to take your time and read through the |
| 175 | [documentation](https://nginx.org/en/docs/). This saved us a lot of headache. | 176 | [documentation](https://nginx.org/en/docs/). This saved us a lot of headache. |
| 176 | Googling for solutions only goes so far. | 177 | Googling for solutions only goes so far. |
| 177 | 178 | ||
| 178 | ## Use Redis/Memcached | 179 | ## Use Redis/Memcached |
| 179 | 180 | ||
| 180 | As explained above we are using caching basically for everything. It is the | 181 | As explained above we are using caching basically for everything. It is the |
| 181 | corner stone of our services. At first we were very careful about the quantity | 182 | corner stone of our services. At first we were very careful about the quantity |
| 182 | of things we stored in [Redis](https://redis.io/). But we later found out that | 183 | of things we stored in [Redis](https://redis.io/). But we later found out that |
| 183 | the memory footprint is very low even when storing large amount of data in it. | 184 | the memory footprint is very low even when storing large amount of data in it. |
| 184 | 185 | ||
| 185 | So we gradually increased our usage to caching whole HTML outputs of dashboard. | 186 | So we gradually increased our usage to caching whole HTML outputs of dashboard. |
| 186 | This improved our performance in order of magnitude. And by using native TTL | 187 | This improved our performance in order of magnitude. And by using native TTL |
| 187 | support this goes hand in hand with our needs. | 188 | support this goes hand in hand with our needs. |
| 188 | 189 | ||
| 189 | The reason why we choose [Redis](https://redis.io/) over [Memcached](https://memcached.org/) | 190 | The reason why we choose [Redis](https://redis.io/) over |
| 190 | was the nature of scalability of Redis out of the box. But all this can be | 191 | [Memcached](https://memcached.org/) was the nature of scalability of Redis out |
| 191 | achieved with Memcached. | 192 | of the box. But all this can be achieved with Memcached. |
| 192 | 193 | ||
| 193 | ## Conclusion | 194 | ## Conclusion |
| 194 | 195 | ||
| 195 | There are a lot more details that could have been written and every single | 196 | There are a lot more details that could have been written and every single topic |
| 196 | topic in here deserves it's own post but you probably got the idea about | 197 | in here deserves it's own post but you probably got the idea about the problems |
| 197 | the problems we faced. | 198 | we faced. |
| 198 | |||
