SlideShare a Scribd company logo
1 of 22
Download to read offline
Scaling up task
processing
with Celery
Stockholm Python User Group – May 7th 2014
Hej, my name is Nicolas Grasset, and I work here at Lifesum.
What is Celery?
“Celery is an asynchronous task queue based on
distributed message passing”
Celery runs on Python (2.5, 2.6, 2.7, 3.2, 3.3) PyPy (1.8, 1.9) and Jython (2.5, 2.7).
It is maintained by Ask Solem, supported by Pivotal and released under BSD License
!
!
@user_required!
def verify_in_app_purchase(request):!
"""!
API to verify in-app purchase against iTunes!
"""!
!
receipt = request.POST.get('receipt', None)!
!
if not receipt:!
raise Http404!
!
# Warning, this takes 2sec on average!
request.user.save_receipt_if_valid( receipt )!
!
return Response({'thank': 'you'})!
!
!
Why task queues?
!
!
@user_required!
def verify_in_app_purchase(request):!
"""!
API to verify in-app purchase against iTunes!
"""!
!
receipt = request.POST.get('receipt', None)!
!
if not receipt:!
raise Http404!
!
# Warning, this takes 2sec on average!
request.user.save_receipt_if_valid( receipt )!
!
return Response({'thank': 'you'})!
!
!
task queues
Distributed?
Producer
Producer
Producer
Broker
Broker
Worker
Worker
Worker
Worker
Worker
…for High Availability and speed
Flask + Celery
Django + Celery
Celery + Celery
Redis
RabbitMQ
Celery
Celery
Celery
Celery
Celery
Celery is simple
!
!
!
!
!
from celery import Celery!
!
app = Celery('hello', broker='amqp://guest@localhost//')!
!
@app.task(ignore_result=True)!
def hello():!
return 'hello world'!
!
hello.delay()!
!
!
!
!
!
10 things I learnt on
the way
1. Start simple
Single broker. Single queue
aptitude install rabbitmq-server
2. Watch for the results
!
!
!
!
!
from celery import Celery!
!
app = Celery('hello', broker='amqp://guest@localhost//')!
!
@app.task(ignore_result=True)!
def hello():!
return 'hello world'!
!
@app.task!
def hello_memory_usage():!
return 'hello world'!
!
!
!
3. Monitor it!
4. Consume tasks faster than
you produce them.
5. Tweaking for concurrency
!
python -m celery worker -E --time-limit=300 -c 64 -P eventlet!
python -m celery worker -E --time-limit=300 -c 8 -P prefork!
python -m celery worker -E --time-limit=300 --autoscale=10,3 --maxtasksperchild=10000!
!
import eventlet!
!
eventlet.monkey_patch()!
eventlet.monkey_patch(MySQLdb=True)!
!
6. Scale worker/producer in pair
MySQL on RDS
with fail-over
Python Application
~2-16 Auto-Scaling
instances
nginx http + celery workers
Elastic Load Balancer
*.lifesum.com
MySQL read-replica
Redis cluster for cache
Sphinx SearchRabbit MQ ElasticSearch
Amazon
CloudFront
S3 Storage Sendgrid.com
Email delivery
getPusher.com
Socket Communication
Parse.com
Mobile Push Notifications
iPhone apps
Android apps
Web users
System architecture, March 2014
7. Keep your tasks clean
!
!
from celery.task import task!
!
@task(ignore_result=True)!
def will_take_some_time(list_of_users, sometimes=False):!
my_favorite_utils(list_of_users, sometimes)!
!
8. Manage DB transactions
!
from django.db import transaction!
from djcelery_transactions import task!
from models import Message!
!
@task(ignore_result=True)!
def mark_messages_as_read(user_id, day):!
with transaction.commit_on_success():!
Message.objects.filter(user_id=user_id, day=day).update(read=True)!
!
!
@task(ignore_result=True)!
def create_messages(list_of_users, day, message):!
with transaction.commit_on_success():!
for user_id in list_of_users:!
Message.objects.create(user_id=user_id, day=day, text=message)!
mark_messages_as_read.delay(user_id, day)!
9. Replace your cron jobs
!
from celery.schedules import crontab!
!
CELERYBEAT_SCHEDULE = {!
# Executes every Monday morning at 7:30 A.M!
'add-every-monday-morning': {!
'task': 'tasks.add',!
'schedule': crontab(hour=7, minute=30, day_of_week=1),!
'args': (16, 16),!
},!
}!
!
python -m celery worker -B!
10. Locks
!
LOCK_EXPIRE = 60 * 5 # Lock expires in 5 minutes!
!
@task(ignore_result=True)!
def delete_messages(user_id):!
!
lock_id = ‘lock-mark_messages_as_read-{0}’.format(user_id)!
acquire_lock = lambda: cache.add(lock_id, 'true', LOCK_EXPIRE)!
release_lock = lambda: cache.delete(lock_id)!
!
if acquire_lock():!
try:!
Message.objects.filter(user_id=user_id).delete()!
finally:!
release_lock()!
return True!
!
logger.warning("Lock was not acquired!")!
Thanks! Reach me by email at nicolas@lifesum.com
or on Twitter @fellowshipofone

More Related Content

What's hot

[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...
[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...
[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...
Shengyou Fan
 
Europython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryEuropython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & Celery
Mauro Rocco
 
Presentation JEE et son écossystéme
Presentation JEE et son écossystémePresentation JEE et son écossystéme
Presentation JEE et son écossystéme
Algeria JUG
 

What's hot (20)

Django Celery
Django Celery Django Celery
Django Celery
 
Azure Service Fabric
Azure Service FabricAzure Service Fabric
Azure Service Fabric
 
Introduction to Coroutines @ KotlinConf 2017
Introduction to Coroutines @ KotlinConf 2017Introduction to Coroutines @ KotlinConf 2017
Introduction to Coroutines @ KotlinConf 2017
 
Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is coming
 
JavaScript Event Loop
JavaScript Event LoopJavaScript Event Loop
JavaScript Event Loop
 
Docker 기반 개발환경 구축 - XE Open seminar #2
Docker 기반 개발환경 구축 - XE Open seminar #2Docker 기반 개발환경 구축 - XE Open seminar #2
Docker 기반 개발환경 구축 - XE Open seminar #2
 
[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...
[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...
[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...
 
Europython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryEuropython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & Celery
 
Azure Redis Cache
Azure Redis CacheAzure Redis Cache
Azure Redis Cache
 
Rxjs ngvikings
Rxjs ngvikingsRxjs ngvikings
Rxjs ngvikings
 
Demystifying Angular Animations
Demystifying Angular AnimationsDemystifying Angular Animations
Demystifying Angular Animations
 
Spring Framework - Spring Security
Spring Framework - Spring SecuritySpring Framework - Spring Security
Spring Framework - Spring Security
 
An Introduction to Celery
An Introduction to CeleryAn Introduction to Celery
An Introduction to Celery
 
JavaScript Event Loop
JavaScript Event LoopJavaScript Event Loop
JavaScript Event Loop
 
以 Kotlin Multiplatform Mobile (KMM) 開發跨平台行動應用
以 Kotlin Multiplatform Mobile (KMM) 開發跨平台行動應用以 Kotlin Multiplatform Mobile (KMM) 開發跨平台行動應用
以 Kotlin Multiplatform Mobile (KMM) 開發跨平台行動應用
 
Presentation JEE et son écossystéme
Presentation JEE et son écossystémePresentation JEE et son écossystéme
Presentation JEE et son écossystéme
 
Node.js File system & Streams
Node.js File system & StreamsNode.js File system & Streams
Node.js File system & Streams
 
Why NGINX Plus/NGINX Controller for NGINX OSS users
Why NGINX Plus/NGINX Controller for NGINX OSS usersWhy NGINX Plus/NGINX Controller for NGINX OSS users
Why NGINX Plus/NGINX Controller for NGINX OSS users
 
Intro to React
Intro to ReactIntro to React
Intro to React
 
Integrating React.js with PHP projects
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projects
 

Similar to Scaling up task processing with Celery

PuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into OperationsPuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into Operations
grim_radical
 
PHP to Python with No Regrets
PHP to Python with No RegretsPHP to Python with No Regrets
PHP to Python with No Regrets
Alex Ezell
 
node.js, javascript and the future
node.js, javascript and the futurenode.js, javascript and the future
node.js, javascript and the future
Jeff Miccolis
 

Similar to Scaling up task processing with Celery (20)

CI Provisioning with OpenStack - Gidi Samuels - OpenStack Day Israel 2016
CI Provisioning with OpenStack - Gidi Samuels - OpenStack Day Israel 2016CI Provisioning with OpenStack - Gidi Samuels - OpenStack Day Israel 2016
CI Provisioning with OpenStack - Gidi Samuels - OpenStack Day Israel 2016
 
Bpstudy20101221
Bpstudy20101221Bpstudy20101221
Bpstudy20101221
 
Sherlock Homepage - A detective story about running large web services - WebN...
Sherlock Homepage - A detective story about running large web services - WebN...Sherlock Homepage - A detective story about running large web services - WebN...
Sherlock Homepage - A detective story about running large web services - WebN...
 
PuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into OperationsPuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into Operations
 
Embulk, an open-source plugin-based parallel bulk data loader
Embulk, an open-source plugin-based parallel bulk data loaderEmbulk, an open-source plugin-based parallel bulk data loader
Embulk, an open-source plugin-based parallel bulk data loader
 
20141210 rakuten techtalk
20141210 rakuten techtalk20141210 rakuten techtalk
20141210 rakuten techtalk
 
PHP to Python with No Regrets
PHP to Python with No RegretsPHP to Python with No Regrets
PHP to Python with No Regrets
 
Dave Orchard - Offline Web Apps with HTML5
Dave Orchard - Offline Web Apps with HTML5Dave Orchard - Offline Web Apps with HTML5
Dave Orchard - Offline Web Apps with HTML5
 
Crossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end FrameworkCrossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end Framework
 
TorqueBox at DC:JBUG - November 2011
TorqueBox at DC:JBUG - November 2011TorqueBox at DC:JBUG - November 2011
TorqueBox at DC:JBUG - November 2011
 
Celery in the Django
Celery in the DjangoCelery in the Django
Celery in the Django
 
Introdution to Node.js
Introdution to Node.jsIntrodution to Node.js
Introdution to Node.js
 
Secure Coding For Java - Une introduction
Secure Coding For Java - Une introductionSecure Coding For Java - Une introduction
Secure Coding For Java - Une introduction
 
node.js, javascript and the future
node.js, javascript and the futurenode.js, javascript and the future
node.js, javascript and the future
 
Node.js
Node.jsNode.js
Node.js
 
Java Hurdling: Obstacles and Techniques in Java Client Penetration-Testing
Java Hurdling: Obstacles and Techniques in Java Client Penetration-TestingJava Hurdling: Obstacles and Techniques in Java Client Penetration-Testing
Java Hurdling: Obstacles and Techniques in Java Client Penetration-Testing
 
Building and Scaling Node.js Applications
Building and Scaling Node.js ApplicationsBuilding and Scaling Node.js Applications
Building and Scaling Node.js Applications
 
OWF12/Java Moussine pouchkine Girard
OWF12/Java  Moussine pouchkine GirardOWF12/Java  Moussine pouchkine Girard
OWF12/Java Moussine pouchkine Girard
 
Google AppEngine @Open World Forum 2012 - 12 oct.2012
Google AppEngine @Open World Forum 2012 - 12 oct.2012Google AppEngine @Open World Forum 2012 - 12 oct.2012
Google AppEngine @Open World Forum 2012 - 12 oct.2012
 
(BDT402) Performance Profiling in Production: Analyzing Web Requests at Scale...
(BDT402) Performance Profiling in Production: Analyzing Web Requests at Scale...(BDT402) Performance Profiling in Production: Analyzing Web Requests at Scale...
(BDT402) Performance Profiling in Production: Analyzing Web Requests at Scale...
 

Recently uploaded

Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Victor Rentea
 

Recently uploaded (20)

[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot ModelMcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital Adaptability
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 

Scaling up task processing with Celery

  • 1. Scaling up task processing with Celery Stockholm Python User Group – May 7th 2014
  • 2. Hej, my name is Nicolas Grasset, and I work here at Lifesum.
  • 4. “Celery is an asynchronous task queue based on distributed message passing”
  • 5. Celery runs on Python (2.5, 2.6, 2.7, 3.2, 3.3) PyPy (1.8, 1.9) and Jython (2.5, 2.7). It is maintained by Ask Solem, supported by Pivotal and released under BSD License
  • 6. ! ! @user_required! def verify_in_app_purchase(request):! """! API to verify in-app purchase against iTunes! """! ! receipt = request.POST.get('receipt', None)! ! if not receipt:! raise Http404! ! # Warning, this takes 2sec on average! request.user.save_receipt_if_valid( receipt )! ! return Response({'thank': 'you'})! ! ! Why task queues?
  • 7. ! ! @user_required! def verify_in_app_purchase(request):! """! API to verify in-app purchase against iTunes! """! ! receipt = request.POST.get('receipt', None)! ! if not receipt:! raise Http404! ! # Warning, this takes 2sec on average! request.user.save_receipt_if_valid( receipt )! ! return Response({'thank': 'you'})! ! ! task queues
  • 9. …for High Availability and speed Flask + Celery Django + Celery Celery + Celery Redis RabbitMQ Celery Celery Celery Celery Celery
  • 10. Celery is simple ! ! ! ! ! from celery import Celery! ! app = Celery('hello', broker='amqp://guest@localhost//')! ! @app.task(ignore_result=True)! def hello():! return 'hello world'! ! hello.delay()! ! ! ! ! !
  • 11. 10 things I learnt on the way
  • 12. 1. Start simple Single broker. Single queue aptitude install rabbitmq-server
  • 13. 2. Watch for the results ! ! ! ! ! from celery import Celery! ! app = Celery('hello', broker='amqp://guest@localhost//')! ! @app.task(ignore_result=True)! def hello():! return 'hello world'! ! @app.task! def hello_memory_usage():! return 'hello world'! ! ! !
  • 15. 4. Consume tasks faster than you produce them.
  • 16. 5. Tweaking for concurrency ! python -m celery worker -E --time-limit=300 -c 64 -P eventlet! python -m celery worker -E --time-limit=300 -c 8 -P prefork! python -m celery worker -E --time-limit=300 --autoscale=10,3 --maxtasksperchild=10000! ! import eventlet! ! eventlet.monkey_patch()! eventlet.monkey_patch(MySQLdb=True)! !
  • 17. 6. Scale worker/producer in pair MySQL on RDS with fail-over Python Application ~2-16 Auto-Scaling instances nginx http + celery workers Elastic Load Balancer *.lifesum.com MySQL read-replica Redis cluster for cache Sphinx SearchRabbit MQ ElasticSearch Amazon CloudFront S3 Storage Sendgrid.com Email delivery getPusher.com Socket Communication Parse.com Mobile Push Notifications iPhone apps Android apps Web users System architecture, March 2014
  • 18. 7. Keep your tasks clean ! ! from celery.task import task! ! @task(ignore_result=True)! def will_take_some_time(list_of_users, sometimes=False):! my_favorite_utils(list_of_users, sometimes)! !
  • 19. 8. Manage DB transactions ! from django.db import transaction! from djcelery_transactions import task! from models import Message! ! @task(ignore_result=True)! def mark_messages_as_read(user_id, day):! with transaction.commit_on_success():! Message.objects.filter(user_id=user_id, day=day).update(read=True)! ! ! @task(ignore_result=True)! def create_messages(list_of_users, day, message):! with transaction.commit_on_success():! for user_id in list_of_users:! Message.objects.create(user_id=user_id, day=day, text=message)! mark_messages_as_read.delay(user_id, day)!
  • 20. 9. Replace your cron jobs ! from celery.schedules import crontab! ! CELERYBEAT_SCHEDULE = {! # Executes every Monday morning at 7:30 A.M! 'add-every-monday-morning': {! 'task': 'tasks.add',! 'schedule': crontab(hour=7, minute=30, day_of_week=1),! 'args': (16, 16),! },! }! ! python -m celery worker -B!
  • 21. 10. Locks ! LOCK_EXPIRE = 60 * 5 # Lock expires in 5 minutes! ! @task(ignore_result=True)! def delete_messages(user_id):! ! lock_id = ‘lock-mark_messages_as_read-{0}’.format(user_id)! acquire_lock = lambda: cache.add(lock_id, 'true', LOCK_EXPIRE)! release_lock = lambda: cache.delete(lock_id)! ! if acquire_lock():! try:! Message.objects.filter(user_id=user_id).delete()! finally:! release_lock()! return True! ! logger.warning("Lock was not acquired!")!
  • 22. Thanks! Reach me by email at nicolas@lifesum.com or on Twitter @fellowshipofone