SlideShare a Scribd company logo
"Distributed graphs and microservices in Prom.ua",  Maksym Kindritskyi
● Кіндріцький Максим
● 6 останніх років у Prom.ua (EVO)
● Team Lead команд User Engagement/Feedback Ecosystem
Привіт
Prom.ua
● Більше 50 тис. підприємців
● 3 млн. унікальних юзерів за добу
● Щодня більше ніж 70 тисяч покупок
● Python backend
● GraphQL > 7 років
● 10k rps на бекенди (моноліт + сервіси)
1. GraphQL + мікросервіси в Prom.ua
2. Проблеми розподіленого GraphQL на gRPC/REST
3. Як зробити розподілений GraphQL на Apollo Federation
4. Які плюси використання Apollo Federation
План
● Хто вже використовує GraphQL у себе в проекті
● Що робити з GraphQL якщо ви плануєте розділяти моноліт на сервіси
● Ви же маєте мікросервіси, але граф ще живе в моноліті
● Вас не влаштовує граф побудований на gRPC
● Ви лише плануєте впроваджувати GraphQL в мікросервіси
Кому це може бути цікаво?
GraphQL
● Мова запитів + рантайм(сервери написані на більшості популярних мов)
● Специфікація https://spec.graphql.org
● Python фреймворки
○ graphene
○ strawberry
○ ariadne
○ hiku
Що таке GraphQL ?
Що таке GraphQL ?
query GetUser {
user(id: 1) {
id
name
dateCreated
}
}
@strawberry.type
class User:
id: int
name: str
date_created: datetime
@strawberry.type
class Query:
@strawberry.field
def user(self, id: int) -> User | None:
return info.context.users.get(id)
GraphQL в Prom.ua
GraphQL + моноліт
User
Cabinet
Monolith
/graphql
GraphQL + моноліт
User
Cabinet
Monolith
/graphql
User
Cabinet
Monolith/
Products
/graphql
Opinions
Service
gRPC
GraphQL запит
query GetProduct {
product(id: 1) {
id
name
opinions {
id
rating
}
}
}
< grpc request
< database request
● Весь граф відгуків все ще знаходиться в моноліті
● Час на розробку фічей збільшився
● Час деплою все ще обмежений найповільнішим сервісом (моноліт)
● Трафік відгуків все ще проходить через моноліт
● Куча серіалізацій - json > dict > protobuf > dataclass > dict > json
● Знову over-fetching
Проблеми з якими ми зіткнулись
Serialize/deserialize pain
@dataclass
class Opinion:
id: int
date_created: datetime
def handle_request(body: str) -> str:
data = json.loads(body)
pb = api.get_opinion(data)
dcl = Opinion(id=pb.id, date_created=pb.date_created.ToDatetime())
result = db.to_dict()
return json.dumps(result)
Server over-fetching
query GetOpinion {
opinion(id: 1) {
id
dateCreated
}
}
message Opinion {
int32 id = 1;
string date_created = 2;
int32 user_id = 3;
...
int32 rating = 20;
}
Нам потрібно рішення яке дозволить:
● Перенести всі потрібні типи з моноліту в мікросервіс
● Зміни в типах і бізнес-логіці в мають бути лише в мікросервісі
● Прибрати частину трафіку з моноліту - щоб дані діставались з
потрібного сервісу
● Деплоїти зміни по нашим функціоналам незалежно від моноліту
● Деплоїти зміни значно швидше
Як вирішити всі ці проблеми ?
Apollo Federation
● З’являється gateway, який вирішує в який сервіс за даними сходити
● Щоб gateway знав про всі сервіси йому потрібна суперграф схема
● Отримати суперграф схему можна скомпозувавши схеми всіх сервісів
● Схема зберігається в GraphQL Registry, до якого gateway має доступ
Apollo Federation
Технологія, яка дозволяє композувати багато сервісів що мають графове
апі в одну велику схему - так званий суперграф.
Як це працює?
Стара архітектура
User
Cabinet
/graphql
Monolith/
Products
Opinions
Service
gRPC
Нова архітектура
User
Cabinet
Apollo
Router
/graphql
Monolith/
Products
Opinions
Service
/graphql
Graphql
Registry
● В сервісі відгуків з’являється /graphql апі і всі його типи тепер тут
○ Тепер типи належать мікросервісу
Що змінилось ?
● Роутер за даними про відгуки ходить в мікросервіс
○ Ми прибрали частину трафіку з моноліту
● Пишемо код і деплоїмо лише сервіс відгуків, моноліт не чіпаємо
○ Зміни по функціоналу відгуків деплояться незалежно від моноліту і це
швидко
● Роутер запитає в мікросервісу відгуків лише ті поля які запитав клієнт
○ Більше немає server over-fetching
● Кожен GraphQL сервіс це Apollo Federation Subgraph
● Всі сервіси пушають свою схему в GraphQL Registry
● GraphQL Registry будує supergraph схему
● Роутер стартує з supergraph схемою
● Всі запити проходять через роутер
Як це працює ?
Subgraph сервер
@strawberry.type
class Product:
id: int
name: str
@strawberry.type
class Query:
products: list[Product] = strawberry.federation.field(resolver=get_products)
schema = strawberry.federation.Schema(query=Query, enable_federation_v2=True)
Apollo Federation Entities
● Entities - це типи, що знаходяться в одному сервісі і можуть
використовуватися в інших сервісах
● Можна розширювати тип одного сервісу в іншому сервісі
GraphQL схема в моноліті
type Product {
id: Int!
name: String!
}
type Product {
id: Int!
name: String!
opinions: [Opinion!]!
}
GraphQL схема в сервісі відгуків
stub type
type Opinion {
id: Int!
rating: Int!
productId: Int!
}
type Product @key("id") {
id: Int!
opinions: [Opinion!]!
}
Код в моноліті
@strawberry.federation.type(keys=["id"])
class Product:
id: int
name: str
@classmethod
def resolve_reference(cls, id: strawberry.ID, info: Info):
product = info.context.products.get(int(id))
return Product(id=product.id, name=product.name)
Код в сервісі відгуків
@strawberry.type
class Opinion:
id: int
product_id: int
@strawberry.federation.type(keys=[“id”])
class Product:
id: int
@classmethod
def resolve_reference(cls, id: strawberry.ID, info: Info):
opinions = info.context.opinions.get_by_product(int(id))
return Product(id=id, opinions=opinions)
N+1 проблема
@strawberry.federation.type(keys=["id"])
class Product:
id: int
name: str
@classmethod
def resolve_reference(cls, id: strawberry.ID, info: Info):
product = info.context.products.get(int(id))
return Product(id=product.id, name=product.name)
Фікс N+1 в strawberry (dataloader)
def load_products(ids: list[int]) -> list[Product]:
return [Product(id=p.id, name=p.name) for p in products.list(ids)]
products_loader = DataLoader(load_products)
@strawberry.federation.type(keys=["id"])
class Product:
id: int
name: str
@classmethod
def resolve_reference(cls, id: strawberry.ID, info: Info):
return info.context.products_loader.load(int(id))
Фікс N+1 в hiku (by design)
product_fq = FieldsQuery("sqla.session", table=Product.__table__)
def resolve_product_reference(representations: list[dict]) -> list[int]:
return [r["id"] for r in representations]
FederatedNode(
"Product",
[Field("id", Integer, product_fq), Field("name", String, product_fq)],
directives=[Key("id")],
resolve_reference=resolve_product_reference,
)
GraphQL запит
query GetProduct {
product(id: 1) {
id
name
opinions {
id
rating
}
}
}
< data from opinion service
< data from product service
CI/CD та GraphQL Registry
Композиція схем сабграфів
● Apollo Rover - CLI tool
● GraphQL Registry + CI/CD
CI/CD
CI/CD
# strawberry export-schema reviews.graph:schema —output schema.graphql
# hive schema:publish —service reviews —url http://reviews.svc/graphql schema.graphql
GraphQL Registry
● Зберігає схеми
● Валідує схеми
● Композує схеми сабграфів у суперграф схему
● Дає апі для витягування схем (альтернатива інтроспекції на
продакшені)
Hive GraphQL Registry
Висновки
В результаті ми не просто вирішили наші проблеми, а і отримали навіть
деякі бенефіти:
● Ми стали релізити фічі швидше ніж при монолітній архітектурі
● Моноліту стало трошки легше, бо ми зняли з нього частину трафіку
● Менший асинхронний сервер + окрема менша база - функціонал
швидший
● В GraphQL Registry ми тепер можемо відслідковувати поля, що більше
не використовуються і видаляти їх безпечно
● Ми перевели декілька сервісів теж на Apollo Federation
Корисні посилання Apollo Federation
● Apollo Federation Spec - https://www.apollographql.com/docs/federation/
● Apollo Router - https://www.apollographql.com/docs/router
● Навчальні матеріали по Federation -
https://www.apollographql.com/tutorials/browse?categories=federation
● Hive GraphQL Registry - https://the-guild.dev/graphql/hive/docs
● How Netflix uses federation (big scale) -
https://www.infoq.com/presentations/netflix-scaling-graphql
Дякую

More Related Content

PPTX
" GraphQL_ The Good parts", Sergii Lischuk.pptx
PPTX
tsql
PPTX
Лекція №12 Передача параметрів у функцію.pptx
PDF
Загальні принципи розроблення АРМ оператора на базі SCADA/HMI
PPTX
ASP.Net basics
PPT
Основи алгоритмізації та програмування. Лекція 1
PPT
Prometheus. Масовий онлайн курс "Основи програмування". Лекція 5
PPSX
Lviv PMDay 2015 S Михайло Попчук: “Синхронізація декількох Agile команд в про...
" GraphQL_ The Good parts", Sergii Lischuk.pptx
tsql
Лекція №12 Передача параметрів у функцію.pptx
Загальні принципи розроблення АРМ оператора на базі SCADA/HMI
ASP.Net basics
Основи алгоритмізації та програмування. Лекція 1
Prometheus. Масовий онлайн курс "Основи програмування". Лекція 5
Lviv PMDay 2015 S Михайло Попчук: “Синхронізація декількох Agile команд в про...

Similar to "Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi (20)

PPT
V24 com to_net
PPTX
PDF
iPhone Objective-C Development (ukr) (2009)
PPTX
Clean code (UA)
PPTX
Design patterns part 2
PDF
Інші підсистеми
PDF
"Incremental rollouts and rollbacks with business metrics control at every st...
PDF
Joomla 3. Що нового для розробників у новій версії - Віталій Маренков
PDF
Павло Юрійчук — Перехід на Angular.js. Howto
PDF
Global logic tech talk switching to Angular.js
PDF
Alina Onyshchuk: Кейс реалізації забезпечення якості (QA) в digital агентстві...
PPT
Using Metatags in Flex Developing
PPTX
Нікіта Загурдаєв – Автоматизація PMO: Практичні рішення та інструменти
PDF
"Rethinking Continuous Delivery", Andrii Nasinnyk
PPTX
Design patterns part 1
PDF
лаб. роб. №2 обєкти та сервіси що ними надаються
PPTX
[Knowledge Sharing] - Microservices Step-by-Step
PDF
Play Mongodb
PDF
Nikita Zahurdaiev: Automating PMO: Practical Tools and Strategies (UA)
V24 com to_net
iPhone Objective-C Development (ukr) (2009)
Clean code (UA)
Design patterns part 2
Інші підсистеми
"Incremental rollouts and rollbacks with business metrics control at every st...
Joomla 3. Що нового для розробників у новій версії - Віталій Маренков
Павло Юрійчук — Перехід на Angular.js. Howto
Global logic tech talk switching to Angular.js
Alina Onyshchuk: Кейс реалізації забезпечення якості (QA) в digital агентстві...
Using Metatags in Flex Developing
Нікіта Загурдаєв – Автоматизація PMO: Практичні рішення та інструменти
"Rethinking Continuous Delivery", Andrii Nasinnyk
Design patterns part 1
лаб. роб. №2 обєкти та сервіси що ними надаються
[Knowledge Sharing] - Microservices Step-by-Step
Play Mongodb
Nikita Zahurdaiev: Automating PMO: Practical Tools and Strategies (UA)
Ad

More from Fwdays (20)

PDF
"Mastering UI Complexity: State Machines and Reactive Patterns at Grammarly",...
PDF
"Effect, Fiber & Schema: tactical and technical characteristics of Effect.ts"...
PPTX
"Computer Use Agents: From SFT to Classic RL", Maksym Shamrai
PPTX
"Як ми переписали Сільпо на Angular", Євген Русаков
PDF
"AI Transformation: Directions and Challenges", Pavlo Shaternik
PDF
"Validation and Observability of AI Agents", Oleksandr Denisyuk
PPTX
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
PDF
"Beyond English: Navigating the Challenges of Building a Ukrainian-language R...
PPTX
"Co-Authoring with a Machine: What I Learned from Writing a Book on Generativ...
PPTX
"Human-AI Collaboration Models for Better Decisions, Faster Workflows, and Cr...
PDF
"AI is already here. What will happen to your team (and your role) tomorrow?"...
PPTX
"Is it worth investing in AI in 2025?", Alexander Sharko
PDF
''Taming Explosive Growth: Building Resilience in a Hyper-Scaled Financial Pl...
PDF
"Scaling in space and time with Temporal", Andriy Lupa.pdf
PDF
"Database isolation: how we deal with hundreds of direct connections to the d...
PDF
"Scaling in space and time with Temporal", Andriy Lupa .pdf
PPTX
"Provisioning via DOT-Chain: from catering to drone marketplaces", Volodymyr ...
PPTX
" Observability with Elasticsearch: Best Practices for High-Load Platform", A...
PPTX
"How to survive Black Friday: preparing e-commerce for a peak season", Yurii ...
PPTX
"Istio Ambient Mesh in production: our way from Sidecar to Sidecar-less",Hlib...
"Mastering UI Complexity: State Machines and Reactive Patterns at Grammarly",...
"Effect, Fiber & Schema: tactical and technical characteristics of Effect.ts"...
"Computer Use Agents: From SFT to Classic RL", Maksym Shamrai
"Як ми переписали Сільпо на Angular", Євген Русаков
"AI Transformation: Directions and Challenges", Pavlo Shaternik
"Validation and Observability of AI Agents", Oleksandr Denisyuk
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
"Beyond English: Navigating the Challenges of Building a Ukrainian-language R...
"Co-Authoring with a Machine: What I Learned from Writing a Book on Generativ...
"Human-AI Collaboration Models for Better Decisions, Faster Workflows, and Cr...
"AI is already here. What will happen to your team (and your role) tomorrow?"...
"Is it worth investing in AI in 2025?", Alexander Sharko
''Taming Explosive Growth: Building Resilience in a Hyper-Scaled Financial Pl...
"Scaling in space and time with Temporal", Andriy Lupa.pdf
"Database isolation: how we deal with hundreds of direct connections to the d...
"Scaling in space and time with Temporal", Andriy Lupa .pdf
"Provisioning via DOT-Chain: from catering to drone marketplaces", Volodymyr ...
" Observability with Elasticsearch: Best Practices for High-Load Platform", A...
"How to survive Black Friday: preparing e-commerce for a peak season", Yurii ...
"Istio Ambient Mesh in production: our way from Sidecar to Sidecar-less",Hlib...
Ad

"Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi

  • 2. ● Кіндріцький Максим ● 6 останніх років у Prom.ua (EVO) ● Team Lead команд User Engagement/Feedback Ecosystem Привіт
  • 3. Prom.ua ● Більше 50 тис. підприємців ● 3 млн. унікальних юзерів за добу ● Щодня більше ніж 70 тисяч покупок ● Python backend ● GraphQL > 7 років ● 10k rps на бекенди (моноліт + сервіси)
  • 4. 1. GraphQL + мікросервіси в Prom.ua 2. Проблеми розподіленого GraphQL на gRPC/REST 3. Як зробити розподілений GraphQL на Apollo Federation 4. Які плюси використання Apollo Federation План
  • 5. ● Хто вже використовує GraphQL у себе в проекті ● Що робити з GraphQL якщо ви плануєте розділяти моноліт на сервіси ● Ви же маєте мікросервіси, але граф ще живе в моноліті ● Вас не влаштовує граф побудований на gRPC ● Ви лише плануєте впроваджувати GraphQL в мікросервіси Кому це може бути цікаво?
  • 7. ● Мова запитів + рантайм(сервери написані на більшості популярних мов) ● Специфікація https://spec.graphql.org ● Python фреймворки ○ graphene ○ strawberry ○ ariadne ○ hiku Що таке GraphQL ?
  • 8. Що таке GraphQL ? query GetUser { user(id: 1) { id name dateCreated } } @strawberry.type class User: id: int name: str date_created: datetime @strawberry.type class Query: @strawberry.field def user(self, id: int) -> User | None: return info.context.users.get(id)
  • 12. GraphQL запит query GetProduct { product(id: 1) { id name opinions { id rating } } } < grpc request < database request
  • 13. ● Весь граф відгуків все ще знаходиться в моноліті ● Час на розробку фічей збільшився ● Час деплою все ще обмежений найповільнішим сервісом (моноліт) ● Трафік відгуків все ще проходить через моноліт ● Куча серіалізацій - json > dict > protobuf > dataclass > dict > json ● Знову over-fetching Проблеми з якими ми зіткнулись
  • 14. Serialize/deserialize pain @dataclass class Opinion: id: int date_created: datetime def handle_request(body: str) -> str: data = json.loads(body) pb = api.get_opinion(data) dcl = Opinion(id=pb.id, date_created=pb.date_created.ToDatetime()) result = db.to_dict() return json.dumps(result)
  • 15. Server over-fetching query GetOpinion { opinion(id: 1) { id dateCreated } } message Opinion { int32 id = 1; string date_created = 2; int32 user_id = 3; ... int32 rating = 20; }
  • 16. Нам потрібно рішення яке дозволить: ● Перенести всі потрібні типи з моноліту в мікросервіс ● Зміни в типах і бізнес-логіці в мають бути лише в мікросервісі ● Прибрати частину трафіку з моноліту - щоб дані діставались з потрібного сервісу ● Деплоїти зміни по нашим функціоналам незалежно від моноліту ● Деплоїти зміни значно швидше Як вирішити всі ці проблеми ?
  • 18. ● З’являється gateway, який вирішує в який сервіс за даними сходити ● Щоб gateway знав про всі сервіси йому потрібна суперграф схема ● Отримати суперграф схему можна скомпозувавши схеми всіх сервісів ● Схема зберігається в GraphQL Registry, до якого gateway має доступ Apollo Federation Технологія, яка дозволяє композувати багато сервісів що мають графове апі в одну велику схему - так званий суперграф. Як це працює?
  • 21. ● В сервісі відгуків з’являється /graphql апі і всі його типи тепер тут ○ Тепер типи належать мікросервісу Що змінилось ? ● Роутер за даними про відгуки ходить в мікросервіс ○ Ми прибрали частину трафіку з моноліту ● Пишемо код і деплоїмо лише сервіс відгуків, моноліт не чіпаємо ○ Зміни по функціоналу відгуків деплояться незалежно від моноліту і це швидко ● Роутер запитає в мікросервісу відгуків лише ті поля які запитав клієнт ○ Більше немає server over-fetching
  • 22. ● Кожен GraphQL сервіс це Apollo Federation Subgraph ● Всі сервіси пушають свою схему в GraphQL Registry ● GraphQL Registry будує supergraph схему ● Роутер стартує з supergraph схемою ● Всі запити проходять через роутер Як це працює ?
  • 23. Subgraph сервер @strawberry.type class Product: id: int name: str @strawberry.type class Query: products: list[Product] = strawberry.federation.field(resolver=get_products) schema = strawberry.federation.Schema(query=Query, enable_federation_v2=True)
  • 24. Apollo Federation Entities ● Entities - це типи, що знаходяться в одному сервісі і можуть використовуватися в інших сервісах ● Можна розширювати тип одного сервісу в іншому сервісі
  • 25. GraphQL схема в моноліті type Product { id: Int! name: String! } type Product { id: Int! name: String! opinions: [Opinion!]! }
  • 26. GraphQL схема в сервісі відгуків stub type type Opinion { id: Int! rating: Int! productId: Int! } type Product @key("id") { id: Int! opinions: [Opinion!]! }
  • 27. Код в моноліті @strawberry.federation.type(keys=["id"]) class Product: id: int name: str @classmethod def resolve_reference(cls, id: strawberry.ID, info: Info): product = info.context.products.get(int(id)) return Product(id=product.id, name=product.name)
  • 28. Код в сервісі відгуків @strawberry.type class Opinion: id: int product_id: int @strawberry.federation.type(keys=[“id”]) class Product: id: int @classmethod def resolve_reference(cls, id: strawberry.ID, info: Info): opinions = info.context.opinions.get_by_product(int(id)) return Product(id=id, opinions=opinions)
  • 29. N+1 проблема @strawberry.federation.type(keys=["id"]) class Product: id: int name: str @classmethod def resolve_reference(cls, id: strawberry.ID, info: Info): product = info.context.products.get(int(id)) return Product(id=product.id, name=product.name)
  • 30. Фікс N+1 в strawberry (dataloader) def load_products(ids: list[int]) -> list[Product]: return [Product(id=p.id, name=p.name) for p in products.list(ids)] products_loader = DataLoader(load_products) @strawberry.federation.type(keys=["id"]) class Product: id: int name: str @classmethod def resolve_reference(cls, id: strawberry.ID, info: Info): return info.context.products_loader.load(int(id))
  • 31. Фікс N+1 в hiku (by design) product_fq = FieldsQuery("sqla.session", table=Product.__table__) def resolve_product_reference(representations: list[dict]) -> list[int]: return [r["id"] for r in representations] FederatedNode( "Product", [Field("id", Integer, product_fq), Field("name", String, product_fq)], directives=[Key("id")], resolve_reference=resolve_product_reference, )
  • 32. GraphQL запит query GetProduct { product(id: 1) { id name opinions { id rating } } } < data from opinion service < data from product service
  • 33. CI/CD та GraphQL Registry
  • 34. Композиція схем сабграфів ● Apollo Rover - CLI tool ● GraphQL Registry + CI/CD
  • 35. CI/CD
  • 36. CI/CD # strawberry export-schema reviews.graph:schema —output schema.graphql # hive schema:publish —service reviews —url http://reviews.svc/graphql schema.graphql
  • 37. GraphQL Registry ● Зберігає схеми ● Валідує схеми ● Композує схеми сабграфів у суперграф схему ● Дає апі для витягування схем (альтернатива інтроспекції на продакшені)
  • 39. Висновки В результаті ми не просто вирішили наші проблеми, а і отримали навіть деякі бенефіти: ● Ми стали релізити фічі швидше ніж при монолітній архітектурі ● Моноліту стало трошки легше, бо ми зняли з нього частину трафіку ● Менший асинхронний сервер + окрема менша база - функціонал швидший ● В GraphQL Registry ми тепер можемо відслідковувати поля, що більше не використовуються і видаляти їх безпечно ● Ми перевели декілька сервісів теж на Apollo Federation
  • 40. Корисні посилання Apollo Federation ● Apollo Federation Spec - https://www.apollographql.com/docs/federation/ ● Apollo Router - https://www.apollographql.com/docs/router ● Навчальні матеріали по Federation - https://www.apollographql.com/tutorials/browse?categories=federation ● Hive GraphQL Registry - https://the-guild.dev/graphql/hive/docs ● How Netflix uses federation (big scale) - https://www.infoq.com/presentations/netflix-scaling-graphql