Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(146)

Side by Side Diff: docs/servicification.md

Issue 2849463006: Add documentation on servicification recipes (Closed)
Patch Set: nit Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | services/README.md » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Servicification Strategies
2
3 This document captures strategies, hints, and best practices for solving typical
4 challenges enountered when converting existing Chromium
5 code to services. It is assumed that you have already read the high-level
6 documentation on [what a service is](/services).
7
8 If you're looking for Mojo documentation, please see the [general
9 Mojo documentation](/mojo) and/or the [documentation on converting Chrome IPC to
10 Mojo](/ipc).
11
12 Note that throughout the below document we link to CLs to illustrate the
13 strategies being made. Over the course of time code tends to shift, so it is
14 likely that the code on trunk does not exactly match what it was at the time of
15 the CLs. When necessary, use the CLs as a starting point for examining the
16 current state of the codebase with respect to these issues (e.g., exactly where
17 a service is embedded within the content layer).
18
19 [TOC]
20
21 ## Questions to Answer When Getting Started
22
23 For the basic nuts and bolts of how to create a new service, see [the
24 documentation on adding a new service](/services#Adding-a-new-service). This
25 section gives questions that you should answer in order to shape the design of
26 your service, as well as hints as to which answers make sense given your
27 situation.
28
29 ### Is your service global or per-BrowserContext?
30 The Service Manager can either:
31
32 - field all connection requests for a given service via the same instance or
33 - create one service instance per user ID.
34
35 Which of these policies the Service Manager employs is determined by the
36 contents of your service manifest: the former is the default, while the latter
Sam McNally 2017/05/01 00:51:03 Did the policy order get swapped?
blundell 2017/05/02 09:00:26 Oops, yup! Thanks for noticing!
37 is selected by informing the Service Manager that your service requires the
38 service_manager:all_users capability([example](https://cs.chromium.org/chromium/ src/services/device/manifest.json)).
39
40 In practice, there is one user ID per-BrowserContext, so the question becomes:
41 Is your Service a global or keyed by BrowserContext? In considering this
42 question, there is one obvious hint: If you are converting per-Profile classes
43 (e.g., KeyedServices), then your service is almost certainly going to be
44 per-user. More generally, if you envision needing to use *any* state related to
45 the user (e.g., you need to store files in the user's home directory), then your
46 service should be per-user.
47
48 Conversely, your service could be a good fit for being global if it is a utility
49 that is unconcerned with the identity of the requesting client (e.g., the [data
50 decoder service](/services/data_decoder), which simply decodes untrusted data in
51 a separate process.
52
53 ### Will you embed your service in //content, //chrome, or neither?
54
55 At the start (and potentially even long-term), your service will likely not
56 actually run in its own process but will rather be embedded in the browser
57 process. This is especially true in the common case where you are converting
58 existing browser-process code.
59
60 You then have a question: Where should it be embedded?
61
62 There are two common choices, //content and //chrome:
63
64 - //content is the default choice. This is where your service should go if
jam 2017/05/01 15:32:25 nit: shouldn't this be based on where the code was
blundell 2017/05/02 09:00:27 Done.
65 the code that you are converting has no //chrome dependencies. Global services
66 are embedded by [content::ServiceManagerContext](https://cs.chromium.org/chrom ium/src/content/browser/service_manager/service_manager_context.cc?type=cs&q=Cre ateDeviceService),
67 while per-user services are naturally embedded by [content::BrowserContext](ht tps://cs.chromium.org/chromium/src/content/browser/browser_context.cc?type=cs&q= CreateFileService).
68
69 - If your service is converting existing //chrome code, then you will need
70 to embed your service in //chrome rather than //content. Global services
71 are embedded by [ChromeContentBrowserClient](https://cs.chromium.org/chromium/ src/chrome/browser/chrome_content_browser_client.cc?type=cs&q=CreateMediaService ),
72 while per-user services are embedded by [ProfileImpl](https://cs.chromium.org/ chromium/src/chrome/browser/profiles/profile_impl.cc?type=cs&q=CreateIdentitySer vice).
73
Ken Rockot(use gerrit already) 2017/04/28 18:07:34 PDF compositor[1] demonstrates another option, whe
blundell 2017/05/02 09:00:26 Done.
74 ### What is your service's threading model?
75
76 By default, your service will run on the IO thread. You can change that by
Ken Rockot(use gerrit already) 2017/04/28 18:07:34 This is only true specifically of services which a
blundell 2017/05/02 09:00:26 Thanks, specified.
77 specifying a task runner as part of the information for constructing your
78 service. In particular, if the code that you are converting is UI-thread code,
79 then you likely want your service running on the UI thread. Look at the changes
80 to profile_impl.cc in [this CL](https://codereview.chromium.org/2753753007) to
81 see an example of setting the task runner that a service should be run on as
82 part of the factory for creating the service.
83
84 ### What is your approach for incremental conversion?
85
86 In creating your service, you likely have two goals:
87
88 - Making the service available to other services
89 - Making the service self-contained
90
91 Those two goals are not the same, and to some extent are at tension:
92
93 - To satisfy the first, you need to build out the API surface of the service to
94 a sufficient degree for the anticipated use cases.
95
96 - To satisfy the second, you need to convert all clients of the code that you
97 are servicifying to instead use the service, and then fold that code into the
98 internal implementation of the service.
99
100 Whatever your goals, you will need to proceed incrementally if your project is
101 at all non-trivial (as they basically all are given the nature of the effort).
102 You should explicitly decide what your approach to incremental bringup and
103 conversion will be. Here some approaches that have been taken for various
104 services:
105
106 - Build out your service depending directly on existing code,
107 convert the clients of that code 1-by-1, and fold the existing code into the
108 service implementation when complete ([Identity Service](https://docs.google.c om/document/d/1EPLEJTZewjiShBemNP5Zyk3b_9sgdbrZlXn7j1fubW0/edit)).
109 - Build out the service with new code and make the existing code
110 into a client library of the service. In that fashion, all consumers of the
111 existing code get converted transparently ([Preferences Service](https://docs. google.com/document/d/1JU8QUWxMEXWMqgkvFUumKSxr7Z-nfq0YvreSJTkMVmU/edit#heading= h.19gc5b5u3e3x)).
112 - Build out the new service piece-by-piece by picking a given
113 bite-size piece of functionality and entirely servicifying that functionality
114 ([Device Service](https://docs.google.com/document/d/1_1Vt4ShJCiM3fin-leaZx00- FoIPisOr8kwAKsg-Des/edit#heading=h.c3qzrjr1sqn7)).
115
116 These all have tradeoffs:
117
118 - The first lets you incrementally validate your API and implementation, but
119 leaves the service depending on external code for a long period of time.
120 - The second can create a self-contained service more quickly, but leaves
121 all the existing clients in place as potential cleanup work.
122 - The third ensures that you're being honest as you go, but delays having
123 the breadth of the service API up and going.
124
125 Which makes sense depends both on the nature of the existing code and on
126 the priorities for doing the servicification. The first two enable making the
127 service available for new use cases sooner at the cost of leaving legacy code in
128 place longer, while the last is most suitable when you want to be very exacting
129 about doing the servicification cleanly as you go.
130
131 ## Platform-Specific Issues
132
133 ### Android
134 As you servicify code running on Android, you might find that you need to port
135 interfaces that are served in Java. Here is an [example CL](https://codereview.c hromium.org/2643713002) that gives a basic
136 pattern to follow in doing this.
137
138 You also might need to register JNI in your service. That is simple to set
139 up, as illustrated in [this CL](https://codereview.chromium.org/2690963002).
140 (Note that that CL is doing more than *just* enabling the Device Service to
141 register JNI; you should take the register_jni.cc file added there as your
142 starting point to examine the pattern to follow).
143
144 Finally, it is possible that your feature will have coupling to UI process state
145 (e.g., the Activity) via Android system APIs. To handle this challenging
146 issue, see the section on [Coupling to UI](#Coupling-to-UI).
147
148 ### iOS
149 The high-level [servicification design doc](https://docs.google.com/document/d/1 5I7sQyQo6zsqXVNAlVd520tdGaS8FCicZHrN0yRu-oU/edit) gives the motivations and
150 vision for supporting services on iOS (in particular, search for the mentions
151 of iOS within the doc).
152
153 Services are not *yet* supported, but this support is being actively being
154 worked on; see [this bug](crbug.com/705982) for the current status. If you have
155 a use case or need for services on iOS, contact blundell@chromium.org.
156
157 ## Client-Specific Issues
158
159 ### Services and Blink
160 Connecting to services directly from Blink is fully supported. [This
161 CL](https://codereview.chromium.org/2698083007) gives a basic example of
162 connecting to an arbitrary service by name from Blink (look at the change to
163 SensorProviderProxy.cpp as a starting point).
164
165 Below, we go through strategies for some common challenges encountered when
166 servicifying features that have Blink as a client.
167
168 #### Mocking Interface Impls in JS
169 It is a common pattern in Blink's layout tests to mock a remote Mojo interface
170 in JS. [This CL](https://codereview.chromium.org/2643713002) illustrates the
171 basic pattern for porting such mocking of an interface hosted by
172 //content/browser to an interface hosted by an arbitrary service (see the
173 changes to mock-battery-monitor.js).
174
175 #### Feature Impls That Depend on Blink Headers
176 In the course of servicifying a feature that has Blink as a client, you might
177 encounter cases where the feature implementation has dependencies on Blink
178 public headers (e.g., defining POD structs that are used both by the client and
179 by the feature implementation). These dependencies pose a challenge:
180
181 - Services should not depend on Blink, as this is a dependency inversion (Blink
182 is a client of services).
183 - However, Blink is very careful about accepting dependencies from Chromium.
184
185 To meet this challenge, you have two options:
186
187 1. Move the code in question from C++ to mojom (e.g., if it is simple structs).
188 2. Move the code into the service's C++ client library, being very explicit
189 about its usage by Blink. See [this CL](https://codereview.chromium.org/24150 83002) for a basic pattern to follow.
190
191 #### Frame-Scoped Connections
192 You must think carefully about the scoping of the connection being made
193 from Blink. In particular, some feature requests are necessarily scoped to a
194 frame in the context of Blink (e.g., geolocation, where permission to access the
195 interface is origin-scoped). Servicifying these features is then challenging, as
196 Blink has no frame-scoped connection to arbitrary services (by design, as
197 arbitrary services have no knowledge of frames or even a notion of what a frame
198 is).
199
200 After a [long
201 discussion](https://groups.google.com/a/chromium.org/forum/#!topic/services-dev/ CSnDUjthAuw),
202 the policy that we have adopted for this challenge is the following:
203
204 CURRENT
205
206 - The renderer makes a request through its frame-scoped connection to the
207 browser.
208 - The browser obtains the necessary permissions before directly servicing the
209 request.
210
211 AFTER SERVICIFYING THE FEATURE IN QUESTION
212
213 - The renderer makes a request through its frame-scoped connection to the
214 browser.
215 - The browser obtains the necessary permissions before forwarding the
216 request on to the underlying service that hosts the feature.
217
218 Notably, from the renderer's POV essentially nothing changes here.
219
220 In the longer term, this will still be the basic model, only with "the browser"
221 replaced by "the Navigation Service" or "the web permissions broker".
222
223 ## Strategies for Challenges to Decoupling from //content
224
225 ### Coupling to UI
226
227 Some feature implementations have hard constraints on coupling to UI on various
228 platforms. An example is NFC on Android, which requires the Activity of the view
229 in which the requesting client is hosted in order to access the NFC platform
230 APIs. This coupling is at odds with the vision of servicification, which is to
231 make the service physically isolatable. However, when it occurs, we need to
232 accommodate it.
233
234 The high-level decision that we have reached is to scope the coupling to the
235 feature *and* platform in question (rather than e.g. introducing a
236 general-purpose FooServiceDelegate), in order to make it completely explicit
237 what requires the coupling and to avoid the coupling creeping in scope.
238
239 The basic strategy to support this coupling while still servicifying the feature
240 in question is to inject a mechanism of mapping from an opaque "context ID" to
241 the required context. The embedder (e.g., //content) maintains this map, and the
242 service makes use of it. The embedder also serves as an intermediary: It
243 provides a connection that is appropriately context-scoped to clients. When
244 clients request the feature in question, the embedder forwards the request on
245 along with the appropriate context ID. The service impl can then map that
246 context ID back to the needed context on-demand using the mapping functionality
247 injected into the service impl.
248
249 To make this more concrete, see [this CL](https://codereview.chromium.org/273494 3003).
250
251 ### Shutdown of singletons
252
253 You might find that your feature includes singletons that are shut down as part
254 of //content's shutdown process. As part of decoupling the feature
255 implementation entirely from //content, the shutdown of these singletons must be
256 either ported into your service or eliminated:
257
258 - In general, as Chromium is moving away from graceful shutdown, the first
259 question to analyze is: Do the singletons actually need to be shut down at
260 all?
261 - If you need to preserve shutdown of the singleton, the naive approach is to
262 move the shutdown of the singleton to the destructor of your service
263 - However, you should carefully examine when your service is destroyed compared
264 to when the previous code was executing, and ensure that any differences
265 introduced do not impact correctness.
266
267 See [this thread](https://groups.google.com/a/chromium.org/forum/#!topic/service s-dev/Y9FKZf9n1ls) for more discussion of this issue.
268
269 ### Tests that muck with service internals
270 It is often the case that browsertests reach directly into what will become part
271 of the internal service implementation to either inject mock/fake state or to
272 monitor private state.
273
274 This poses a challenge: As part of servicification, *no* code outside the
275 service impl should depend on the service impl. Thus, these dependencies need to
276 be removed. The question is how to do so while preserving testing coverage.
277
278 To answer this question, there are several different strategies. These
279 strategies are not mutually-exclusive; they can and should be combined to
280 preserve the full breadth of coverage.
281
282 - Blink client-side behavior can be tested via [layout tests](https://codereview .chromium.org/2731953003)
283 - To test service impl behavior, create [service tests](https://codereview.chrom ium.org/2774783003).
284 - To preserve tests of end-to-end behavior (e.g., that when Blink makes a
285 request via a Web API in JS, the relevant feature impl receives a connection
286 request), we are planning on introducing the ability to register mock
287 implementations with the Service Manager.
288
289 To emphasize one very important point: it is in general necessary to leave
290 *some* test of end-to-end functionality, as otherwise it is too easy for bustage
291 to slip in via e.g. changes to how services are registered. See [this thread](ht tps://groups.google.com/a/chromium.org/forum/#!topic/services-dev/lJCKAElWz-E)
292 for further discussion of this point.
OLDNEW
« no previous file with comments | « no previous file | services/README.md » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698