-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ArtifactStore implementation for CosmosDB #3562
Conversation
3ec5d84
to
e76b758
Compare
Test suite run resultIn a separate branch travis is configured to run whole OW test suite as being run currently for master with default
|
Makes sense to update wskadmin to also handle momdodb/cosmosdb ? |
Yes this aspect was discussed sometime back here. Need to figure out right way to abstract that in python and refactor wskadmin. Would do that in a separate PR |
108d63f
to
2a96e05
Compare
Indexing PolicyCosmosDB provides rich indexing options which support
Currently the view based indexes in CouchDB are eventually consistent and all the test around queries account for that. Based on usage following indexes are configured Subjects
Key points
Whisks
Key points
Activations
Key points
Index policy updatesCurrent logic upon system start would query for existing indexes configured on the collection. If there is a mismatch between what is expected and what is currently found it would provision the new indexes. CosmosDB currently shows a strange behavior where it is creating extra indexes for same path. For e.g. CosmosDB supports online index policy changes where by change in index definition would be performed asynchronously and online allowing the collection to remain available for writes while transformation is in progress. However if the application makes query relying on new indexes then those queries would fail untill indexing completes based on new index definition. So in future if any change in index definition is done then it should be provisioned prior to deploying new code relying on that index. Current approach of checking for index definition would ensure that index definitions are consistent |
301a79a
to
279f2a5
Compare
common/scala/build.gradle
Outdated
|
||
compile 'io.reactivex:rxscala_2.11:0.26.5' | ||
compile 'io.reactivex:rxjava-reactive-streams:1.2.1' | ||
compile 'com.microsoft.azure:azure-cosmosdb:1.0.0' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you have info on the dependency tree that these bring? if there is overlap with existing libs we may want to exclude some, or at least be explicit where they come from
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Below is the dependency tree being pulled in (see here for full tree)
+--- io.reactivex:rxscala_2.11:0.26.5
| +--- org.scala-lang:scala-library:2.11.8 -> 2.11.12
| \--- io.reactivex:rxjava:1.2.4 -> 1.3.3
+--- io.reactivex:rxjava-reactive-streams:1.2.1
| +--- io.reactivex:rxjava:1.2.2 -> 1.3.3
| \--- org.reactivestreams:reactive-streams:1.0.0 -> 1.0.2
\--- com.microsoft.azure:azure-cosmosdb:1.0.0
+--- com.fasterxml.jackson.core:jackson-databind:2.9.4 (*)
+--- com.fasterxml.uuid:java-uuid-generator:3.1.4
+--- org.json:json:20140107
+--- commons-io:commons-io:2.5 -> 2.6
+--- com.github.davidmoten:rxjava-extras:0.8.0.11
| \--- io.reactivex:rxjava:1.3.3
+--- io.reactivex:rxjava:1.3.3
+--- io.reactivex:rxjava-string:1.1.1
| \--- io.reactivex:rxjava:1.2.3 -> 1.3.3
+--- io.reactivex:rxnetty:0.4.20
| +--- io.reactivex:rxjava:1.2.2 -> 1.3.3
| +--- io.netty:netty-codec-http:4.1.5.Final -> 4.1.20.Final
| | \--- io.netty:netty-codec:4.1.20.Final
| | \--- io.netty:netty-transport:4.1.20.Final
| | +--- io.netty:netty-buffer:4.1.20.Final
| | | \--- io.netty:netty-common:4.1.20.Final
| | \--- io.netty:netty-resolver:4.1.20.Final
| | \--- io.netty:netty-common:4.1.20.Final
| +--- io.netty:netty-handler:4.1.5.Final -> 4.1.20.Final
| | +--- io.netty:netty-buffer:4.1.20.Final (*)
| | +--- io.netty:netty-transport:4.1.20.Final (*)
| | \--- io.netty:netty-codec:4.1.20.Final (*)
| \--- org.slf4j:slf4j-api:1.7.6 -> 1.7.25
+--- io.netty:netty-codec-http:4.1.20.Final (*)
+--- io.netty:netty-handler:4.1.20.Final (*)
+--- io.netty:netty-transport:4.1.20.Final (*)
+--- org.slf4j:slf4j-api:1.7.6 -> 1.7.25
\--- org.apache.commons:commons-lang3:3.5
Further did a diff on jars being packaged in controller.tar
30a31
> azure-cosmosdb-1.0.0.jar
34a36
> commons-lang3-3.5.jar
41,43c43,45
< jackson-annotations-2.7.0.jar
< jackson-core-2.7.7.jar
< jackson-databind-2.7.7.jar
---
> jackson-annotations-2.9.0.jar
> jackson-core-2.9.4.jar
> jackson-databind-2.9.4.jar
46c48
< java-uuid-generator-3.1.3.jar
---
> java-uuid-generator-3.1.4.jar
52a55
> json-20140107.jar
69a73,79
> netty-buffer-4.1.20.Final.jar
> netty-codec-4.1.20.Final.jar
> netty-codec-http-4.1.20.Final.jar
> netty-common-4.1.20.Final.jar
> netty-handler-4.1.20.Final.jar
> netty-resolver-4.1.20.Final.jar
> netty-transport-4.1.20.Final.jar
78a89,94
> rxjava-1.3.3.jar
> rxjava-extras-0.8.0.11.jar
> rxjava-reactive-streams-1.2.1.jar
> rxjava-string-1.1.1.jar
> rxnetty-0.4.20.jar
> rxscala_2.11-0.26.5.jar
So following existing dependency versions are getting updated
- jackson - 2.7 -> 2.9 - (minor). So far jackson was being used by kubernetes-client
- java-uuid-generator - 3.1.3 -> 3.1.4 (micro)
Following new set of library being included
- netty - 4.1.20 - 7 jars
- rxjava - 6
- commons-lang3
- json - This would get replaced with jackson post Avoid usage of org.json library Azure/azure-cosmosdb-java#29
In total post this change
- Number of jars in controller increase from 92 -> 108
- Size increases from 89M -> 94M
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only concern is the jump in netty version and including both versions - when I build controller.tar from your branch I get both netty versions:
-rw-r--r-- 0 0 0 413674 May 18 13:27 controller/lib/rxnetty-0.4.20.jar
-rw-r--r-- 0 0 0 562559 May 18 13:27 controller/lib/netty-codec-http-4.1.22.Final.jar
-rw-r--r-- 0 0 0 374389 May 18 13:27 controller/lib/netty-handler-4.1.22.Final.jar
-rw-r--r-- 0 0 0 316316 May 18 13:27 controller/lib/netty-codec-4.1.22.Final.jar
-rw-r--r-- 0 0 0 456605 May 18 13:27 controller/lib/netty-transport-4.1.22.Final.jar
-rw-r--r-- 0 0 0 270720 May 18 13:27 controller/lib/netty-buffer-4.1.22.Final.jar
-rw-r--r-- 0 0 0 32276 May 18 13:27 controller/lib/netty-resolver-4.1.22.Final.jar
-rw-r--r-- 0 0 0 575748 May 18 13:27 controller/lib/netty-common-4.1.22.Final.jar
-rw-r--r-- 0 0 0 1292696 Sep 13 2017 controller/lib/netty-3.10.6.Final.jar
Edit: Seems netty 3 and 4 use different java packages so I guess it should not be a problem
logging: Logging, | ||
materializer: ActorMaterializer): (String, DocumentHandler, CosmosDBViewMapper) = { | ||
entityType.runtimeClass match { | ||
case x if x == classOf[WhiskEntity] => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a better way? does case x:WhiskEntity =>
not work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would work if x is an instance of WhiskEntity
. However here x is class representing WhiskEntity
. So current approach has to be taken
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given the matching and if
predicates, might as well use if/else directly for brevity.
} | ||
} | ||
|
||
private def getOrCreateReference(config: CosmosDBConfig) = synchronized { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get the feeling this reference is special - can you outline the goals of ReferenceCounted
and synchronized
?
nm: I see the "Shutdown Handling" comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ArtifactStore
has a shutdown()
function - is there a reason we cannot use that instead of the specialized ref counting? (client is responsible for shutdown). I don't see any non-test usages of shutdown
currently - what is the hazard, if any, in either treating this the same as couchdb (no shutdown called, except for test cases) OR requiring that clients invoke shutdown (e.g. via sys.addShutdownHook(store.shutdown())
in controller/invoker)?
Part of the diff in client handling from couchdb is the client sharing - you mention that stores share the same AsyncDocumentClient instance, but it's not clear why?
Just looking for ways to simply this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After reading some of the cosmosdb doco (sorry, cosmos n00b here), I see now that the "databases" are modeled as "multiple collections within a single db" which partly explains the client sharing, but I'm still wondering what is the impact if we create per-collection clients?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each AsyncDocumentClient
encapsulates a HttpClient which own a pool of HTTP Connections to CosmosDB. As for all 3 collections we are connecting to same url at backend it would be better to share the pool.
Compared to this in CouchDBRestStore
the pool is managed on per ActorSystem
level which by design is then shared across various ArtifactStore
implementations. In PoolingRestClient#shutdown method you would see a note around not closing the pool assuming shutdown is only relevant for tests. So ref counted approach used in this PR is meant to solve that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getOrCreateReference is called once at create time - is there concurrency here in making the stores that requires synchronization or is this mostly defensive to get the counts right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add a comment explaining the synchronized fwiw.
object CosmosDBArtifactStoreProvider extends ArtifactStoreProvider { | ||
type DocumentClientRef = ReferenceCounted[ClientHolder]#CountedReference | ||
private lazy val config = loadConfigOrThrow[CosmosDBConfig](ConfigKeys.cosmosdb) | ||
private var clientRef: ReferenceCounted[ClientHolder] = _ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does config
need to be lazy? is there any reason not to make it non-lazy, and create the clientRef immediately here? (then no need for the special getOrCreateReference
function)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some of the test setups I am using different database name and thus have a separate method which takes config explicitly. Making it non lazy would cause issue if default config is not complete causing the class load to fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't that OK? (to fail in case of invalid config?) It would only happen if CosmosDB is enabled, and the config is invalid. The tests can always provide a valid non-default config.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even in that case getOrCreateReference
may be required as currently there can be multiple shutdown calls made. In case of object level instance we would need to change the shutdown approach and move it to provider level
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I favor multiple shutdown calls vs the reference tracking.
I tested changing the provider to:
private val config = loadConfigOrThrow[CosmosDBConfig](ConfigKeys.cosmosdb)
private val clientRef = createClient(config)
and it seems to run fine in CosmosDBArtifactStoreTests
- it ends up calling client.close()
multiple times, as expected, and seems to operate properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think post close
no other operation is performed so it does not show up any issue. All clients are closed in affterAll
i.e. once all methods in suite have run
* CosmosDB id considers '/', '\' , '?' and '#' as invalid. EntityNames can include '/' so | ||
* that need to be escaped. For that we use '|' as the replacement char | ||
*/ | ||
protected def escapeId(id: String): String = id.replace("/", "|") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this requires that '|' cannot be used in the id at all? (that is ok by me, but should be validated in escapeId
and unescapeId
, I think)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. So far this was checked in EntityName#entityNameMatcher
however would be good to check the invariant in util method also
Added a check with test for this logic
Codecov Report
@@ Coverage Diff @@
## master #3562 +/- ##
==========================================
- Coverage 74.6% 70.91% -3.69%
==========================================
Files 138 146 +8
Lines 6461 6891 +430
Branches 397 427 +30
==========================================
+ Hits 4820 4887 +67
- Misses 1641 2004 +363
Continue to review full report at Codecov.
|
ea2a857
to
ebe39d3
Compare
ebe39d3
to
ecc82cb
Compare
This seems good to me, would like some others to chime in. My questions are mainly around:
On this last one, I think it is a defect of |
One benefit of this approach is that future test using |
ecc82cb
to
5d303e9
Compare
I think adding |
@tysonnorris I agree current ref count based shutdown handling is not desirable and we should relook into how |
ab35cd0
to
48d3c07
Compare
29fd125
to
5eb2c29
Compare
I am working on a separate PR which would add new stages to run full test suite with Cosmos. That would allow complete test coverage against this new store. Would raise it once this PR is merged |
3d71de7
to
7eb9d23
Compare
Refactored the CosmosDBUtil to a trait and object to simplify imports of utility methods
Use DummyImplicit to enable method overloading as DocId is of type AnyVal and post erasure there is ambiguity in which `newRequestOption` to use. DummyImplicit acts as a workaround for such a case
Also remove TODO for batching as that cannot be supported
This would simplify pruning of orphan dbs via cosmosDbUtil.py script
Use lower size for Cosmos as max limit is 2MB. For ArtifactStore configured with AttachmentStore use large size like 5MB
562d448
to
77991a4
Compare
logging: Logging, | ||
materializer: ActorMaterializer): (String, DocumentHandler, CosmosDBViewMapper) = { | ||
entityType.runtimeClass match { | ||
case x if x == classOf[WhiskEntity] => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given the matching and if
predicates, might as well use if/else directly for brevity.
} | ||
} | ||
|
||
private def getOrCreateReference(config: CosmosDBConfig) = synchronized { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getOrCreateReference is called once at create time - is there concurrency here in making the stores that requires synchronization or is this mostly defensive to get the counts right?
|
||
private def getOrCreateDatabase(): Database = { | ||
client | ||
.queryDatabases(querySpec(config.db), null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it possible to avoid null
and use None (viz Option) instead?
we've generally stayed away from null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
queryDatabases
is java api of Azure CosmosDB driver which uses the null
based approach. So not possible to use Option here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getOrCreateReference is called once at create time
Added some comments. This method may be called concurrently multiple times at startup as various store instances are initialized. Hence need to synchronize
it to ensure all those stores are backed by same client
} | ||
} | ||
|
||
private def getOrCreateReference(config: CosmosDBConfig) = synchronized { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add a comment explaining the synchronized fwiw.
} | ||
|
||
protected def querySpec(id: String) = | ||
new SqlQuerySpec("SELECT * FROM root r WHERE r.id=@id", new SqlParameterCollection(new SqlParameter("@id", id))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment? what is this for...
} | ||
|
||
private def addToMap(name: String, map: Map[String, _]): Map[String, Any] = name.split('.').toList match { | ||
case Nil => sys.error(s"Should not reach here $name") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
throw exception directly instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
String split should never result in empty list. Changed it to throw ISE
* }}} | ||
* Here it uses {{{r['keyName']}}} notation to avoid issues around using reserved words as field name | ||
*/ | ||
def prepareFieldClause(fields: Iterable[String]): String = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be wrapped with a Try for failures (via one of the invariant violations below)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note to self: check unit tests for this trait.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The invariant should never fail as string split can not result in empty list. So should be fine without using Try
. The tests are in CosmosDBUtilTest
|
||
private def dec(): Unit = { | ||
val newCount = count.decrementAndGet() | ||
if (newCount == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
defensively <=
?
val newCount = count.decrementAndGet() | ||
if (newCount == 0) { | ||
inner.close() | ||
count.decrementAndGet() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you comment the second decrement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second decrement make it negative which ensures that future reference
calls result in error
@chetanmeh as discussed on slack I think we need to document the design considerations container here in (configuration and dev docs). |
Thanks a lot @rabbah and @tysonnorris for reviewing this big PR ✨. Opened #3872 to track documentation related work |
This commit provides a CosmosDB based implementation for ArtifactStore SPI. Given the complexity involved in performing various operations against CosmosDB this commit uses the Java SDK to simplify and speed up the implementation - because compared to CouchDB, performing queries with CosmosDB requires client side computation, which involves sending queries to each partition, then collecting and merging the result set. The Async Java SDK takes care of all these interactions and provides a simplified reactive API based on RxJava.
This PR provides a
CosmosDBArtifactStore
implementation to enable using CosmosDB for storing subjects, whisks and activation in Azure CosmosDBThis is currently work in progress and opening this early so as to get feedback on design progress
Build Results
This PR requires test which need account keys to access CosmosDB. Untill it gets merged to master following build runs are being done in my fork with keys configured
CouchDBArtifactStoreTests
based on test suite introduced with MemoryArtifactStore for unit testing and ArtifactStore SPI Validation #3517ArtifactStoreProvider
set toCosmosDBArtifactStoreProvider
. Some test here are being ignored. See below for more detailsFor test coverage see here
Description
Currently OpenWhisk supports CouchDB for storing various entities like actions, rules, subjects, activations etc. This PR provides a CosmosDB based implementation for ArtifactStore SPI
Usage of CosmosDB Java SDK
CosmosDB supports various modes to connect to it. For our usage we have two options
Compared to CouchDB performing queries against CosmosDB requires client side computation which involves sending queries to each partition and then collect and merge the result set. The Async Java SDK takes care of all these interactions and provides a simplified reactive api based on RxJava
Given the complexity involved in performing various operations against CosmosDB this PR uses the Java SDK to simplify and speed up the implementation
Design Considerations
Data Model
Below is the json representation of
whisk.system/utils/echo
action saved in CouchDB and CosmosDBBelow are some keys aspects on how data is stored in CosmosDB
id
The id field in CosmosDB is referred as
id
(in CouchDB its_id
). Per Resource Id docsAs
/
cannot be usedCosmosDBArtifactStore
would replace/
with|
. Sowhisk.system/utils/echo
is stored aswhisk.system|utils|echo
Per OpenWhisk Naming Restrictions
So no other char needs this treatment
Revision Handling
CosmosDB supports optimistic concurrency control (OCC) through HTTP entity tags, or ETags. So for all practical purpose
_rev
is mapped to_etag
field of CosmosDB and all semantics around concurrent updates are supported via that.Computed Fields
CosmosDBArtifactStore
uses a subdocument under_c
to store some computed fields which are referred in queries. Such fields are computed by CouchDB view logic and here these are set at time of inserting/updating the docs viaArtifactStore
API.So any logic which directly manipulates the objects need to also support these fields
Limits
Following are some of the limits enforced by CosmosDB. Currently OpenWhisk does not enforce any restriction on some of these so there is a change in behaviour
Partitioning Strategy
One of the key aspect of using CosmosDB is to determine the partitioning key. This can be decided on per collection basis
For now this PR uses
/id
itself also as the partitioning key. This has following pros and consCons
Note that currently query in CouchDB also hits all partitions and thus performance decreases when number of partitions are large. There are some discussions to support querying for specific partitions. That would make it similar to CosmosDB.
Pros
From production usage perspective OpenWhisk does not perform any query in critical path of activation. There are calls to DB are based on specific id lookups. This enables that read load would be distributed across actual partitions and thus would lead to fair utilization of resource units.
If we use
namespace
as partition key then it can lead to situation where if number of activity related to that namespace increase considerably then it may become hot and thus exhause the resource quota. Requests to the same partition key can't exceed the provisioned throughput allocated to a partition and will be rate-limited.Given that queries are mostly performed during authoring workflow and increase in latency in query responses may be ok
Attachment Storage
As part of this PR the attachments are being stored in CosmosDB itself using its attachment support. This is similar to how attachments are supported in CouchDB. This enables storing upto 10GB of attachments.
Going forward once AttachmentStore SPI PR gets merged this would be change to use the SPI with default implementation using CosmosDB attachment support with an option to use say S3/Azure BlobStore based SPI implementation
Query Execution
CosmosDBArtifactStore
maps the CouchDB views to CosmosDB Queries. Following are details around how the views are mappedGeneric Whisks View
Above example shows how a
whisks.v2.1.0/actions
view is mapped to query. Few things to note hereend
is a reserved word.So
CosmosDBUtil
would use the mode by default for all fields selectedThis enables computing some fields like
exec
on server side viaDocumentHandler.transformViewResult
Similar sql is generated for following views
For above view the query is performed by matching namespace in full
r.namespace
or on root namespacer._c.rootns
andr.entityType
set to required typePublic Packages Query
For public package there is a check for
binding
Activations Query
For
whisks-filters.v2.1.0/activations
view the query is made onr._c.nspath
which is a computed field based view logicSubject Query
subjects/identities
At time of authentication
subjects/identities
view is used. For that below query is invoked which makes use of INNER JOIN to query onnamespaces
sub documentnamespaceThrottlings/blockedNamespaces
This view is used by
NamespaceBlacklist
logic to peridically query for blacklisted namespaceShutdown Handling
All
CosmosDBArtifactStore
instances (3 in total for whisks, activations and subjects) share the sameAsyncDocumentClient
instance. So to handle theshutdown
call properly this PR uses aReferenceCounted
approach where all instance share same reference and once the reference count reaches zero the client instance is closedClient side skipping
ArtifactStore.query
API supportsskip
to support paginated query over large dataset. For CouchDB the query is handled by one of the shards which in turn queries all the other shards and then merges the result on server side (i.e. scatter and gather)For CosmosDB the query results are collected from various partitions and merged by the sdk. Thus
skip
is implemented by dropping the query results. Due to this going to later pages in a query result would be slow.Batching not supported
ArtifactStoreProvider.makeStore
exposes abatching
flag to enable batch insert mode for activations. CosmosDB does not have a bulk insert api so this flag is currently ignored.Azure docs suggest using stored procedures to achive bulk insert support. However that requires all document meant to be inserted to be part of same partition. With our usage of
id
field as partition key this would not help muchTesting Approach
Some of the tests from this PR need to access the CosmosDB service and thus require the account credentials. These credentials would be provided by environment variables. Based on discussion on dev following appraoch is taken.
Pending Work
Third Party Dependencies
This PR pulls in following major dependencies
Dependency on org.json
org.json
dependency library license is marked as Category-X and hence such a library cannot be used as a dependency in Apache Project. For this Azure/azure-cosmosdb-java#29 has been opened and is being addressedTrying out
To try out the CosmosDB integration locally (for now only tests) follow steps below
URI
andPRIMARY KEY
COSMOSDB_KEY
- Use thePRIMARY KEY
value hereCOSMOSDB_ENDPOINT
- Use theURI
value hereCOSMOSDB_NAME
- Set it toopenwhisk
With this it would be possible to run
CosmosDBArtifactStoreTests
locally.Once #3689 is done it would be possible to run complete OpenWhisk setup using CosmosDB via ansible setup scripts
Related issue and scope
My changes affect the following components
Types of changes
Checklist: