sfackler / r2d2 Goto Github PK
View Code? Open in Web Editor NEWA generic connection pool for Rust
License: Apache License 2.0
A generic connection pool for Rust
License: Apache License 2.0
Hi,
First: Great and very easy to use crate! Thanks for that!
This more a question than a issue: Is it possible to resize the pool without loosing the established connections? Of course I could build a new pool but this would result in dropping of all the already established connections. In my use case I'd like to initialise the pool with a small number of connections because of timing restrictions (without disabling the fail fast feature in order to get instant feedback) and increase the size later as needed up to a certain limit.
Don't know if it is important: I use the r2d2 Redis crate.
Thanks,
I have a connection pool configured like this:
let pool = r2d2::Pool::builder()
.min_idle(Some(1))
.idle_timeout(Some(Duration::from_secs(30)))
.build(manager)?;
I expected this would approximately match my peak concurrent connection usage (ie. if I never use more than 3 concurrent connections, I would expect my connection usage to sit around 3).
However, in practice I've found that the connection pool starts around the expected size, but then after some time grows to whatever is the upper limit on connections.
I have nine different services deployed to my cluster, all using r2d2, all using the exact same connection pool configuration, and each one with 2-4 replicas. These services are divided into two types: those with background workers (ie. continually polling the database) and those without (pure request/response based).
All of the services with background workers eat up their maximum allocation of connections once they've been running for any non-trivial amount of time. I can also see that all of the connections have been recently active (within the last few seconds), so it's not a question of "leaking" zombie connections somehow.
These services with background workers only use a single thread for polling the database, so despite the fact that they perform constant DB access, they should never use more than 1 concurrent connection when idle.
Based on this, it appears that there's an issue with the way connections are given out from the pool - instead of giving out the "most recently used" connection for reuse, it must be giving out older or random connections from the pool, keeping all of them alive even when not needed.
I'm having an issue with a long-running application that the connections made by r2d2 are not dropped after I drop the last reference to the pool. After digging through the code, I found that new_inner
creates a second copy of the Arc
here, which is then moved into the closure passed to read_connections
. As far as I can tell, that closure is never dropped, which means the second reference to the Arc
is never dropped, which in turn prevents the SharedPool
the Arc
wraps from being dropped. Since the SharedPool
is never dropped, the workers are never stopped, and so the connections are all left open indefinitely.
With some debug statements inserted into r2d2 (patch below), I get the following output for my application:
new_inner::step s: 1, w: 0
new_inner::step s: 51, w: 0
pool worker starting
pool worker starting
pool worker starting
pool worker got job
...
pool worker got job
pool worker waiting
pool worker waiting
pool worker waiting
new_inner::step s: 1, w: 0
new_inner::step s: 2, w: 0
new pool (starts at s: 2, w: 0)
pool worker waiting
cloning pool (now s: 3, w: 0)
pool worker waiting
pool worker waiting
dropping instance of pool (was s: 3, w: 0)
cloning pool (now s: 3, w: 0)
cloning pool (now s: 4, w: 0)
dropping instance of pool (was s: 4, w: 0)
cloning pool (now s: 4, w: 0)
cloning pool (now s: 5, w: 0)
dropping instance of pool (was s: 5, w: 0)
cloning pool (now s: 5, w: 0)
cloning pool (now s: 6, w: 0)
dropping instance of pool (was s: 6, w: 0)
cloning pool (now s: 6, w: 0)
dropping instance of pool (was s: 6, w: 0)
dropping instance of pool (was s: 5, w: 0)
dropping instance of pool (was s: 4, w: 0)
dropping instance of pool (was s: 3, w: 0)
dropping instance of pool (was s: 2, w: 0)
all done -- sleeping...
Notice in particular how, by the time Pool::new
returns, the Arc
already has two strong references, and how, when my application drops its last reference (right before the "all done" line), there is still a leftover strong reference.
Debug patch:
diff --git a/src/lib.rs b/src/lib.rs
index a620344..6df3284 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -40,6 +40,7 @@
//! ```
#![warn(missing_docs)]
#![doc(html_root_url="https://sfackler.github.io/r2d2/doc/v0.6.3")]
+#![feature(arc_counts)]
#[macro_use]
extern crate log;
@@ -175,10 +177,13 @@ struct SharedPool<M>
thread_pool: ScheduledThreadPool,
}
impl<M> Drop for SharedPool<M> where M: ManageConnection
{
fn drop(&mut self) {
+ println!("dropping shared pool");
self.thread_pool.clear();
+ println!("dropped shared pool");
}
}
@@ -258,14 +263,33 @@ fn reap_connections<M>(shared: &Arc<SharedPool<M>>)
pub struct Pool<M: ManageConnection>(Arc<SharedPool<M>>);
/// Returns a new `Pool` referencing the same state as `self`.
impl<M> Clone for Pool<M> where M: ManageConnection
{
fn clone(&self) -> Pool<M> {
- Pool(self.0.clone())
+ use std::sync;
+ let p = Pool(self.0.clone());
+ println!("cloning pool (now s: {}, w: {})",
+ sync::Arc::strong_count(&self.0),
+ sync::Arc::weak_count(&self.0));
+ p
}
}
+impl<M> Drop for Pool<M>
+ where M: ManageConnection
+{
+ fn drop(&mut self) {
+ use std::sync;
+ println!("dropping instance of pool (was s: {}, w: {})",
+ sync::Arc::strong_count(&self.0),
+ sync::Arc::weak_count(&self.0));
+ }
+}
+
+
impl<M> fmt::Debug for Pool<M> where M: ManageConnection + fmt::Debug
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let inner = self.0.internals.lock().unwrap();
@@ -289,7 +314,15 @@ impl<M> Pool<M> where M: ManageConnection
pub fn new(config: Config<M::Connection, M::Error>,
manager: M)
-> Result<Pool<M>, InitializationError> {
- Pool::new_inner(config, manager, 30)
+ use std::sync;
+ let p = Pool::new_inner(config, manager, 30);
+ if p.is_ok() {
+ let p = p.as_ref().unwrap();
+ println!("new pool (starts at s: {}, w: {})",
+ sync::Arc::strong_count(&p.0),
+ sync::Arc::weak_count(&p.0));
+ }
+ p
}
// for testing
@@ -297,6 +330,7 @@ impl<M> Pool<M> where M: ManageConnection
manager: M,
reaper_rate: i64)
-> Result<Pool<M>, InitializationError> {
+ use std::sync;
let internals = PoolInternals {
conns: VecDeque::with_capacity(config.pool_size() as usize),
num_conns: 0,
@@ -311,6 +345,10 @@ impl<M> Pool<M> where M: ManageConnection
cond: Condvar::new(),
});
+ println!("new_inner::step s: {}, w: {}",
+ sync::Arc::strong_count(&shared),
+ sync::Arc::weak_count(&shared));
+
let initial_size = shared.config.min_idle().unwrap_or(shared.config.pool_size());
{
let mut inner = shared.internals.lock().unwrap();
@@ -320,6 +358,10 @@ impl<M> Pool<M> where M: ManageConnection
drop(inner);
}
+ println!("new_inner::step s: {}, w: {}",
+ sync::Arc::strong_count(&shared),
+ sync::Arc::weak_count(&shared));
+
if shared.config.initialization_fail_fast() {
let end = SteadyTime::now() + cvt(shared.config.connection_timeout());
let mut internals = shared.internals.lock().unwrap();
@@ -336,12 +378,20 @@ impl<M> Pool<M> where M: ManageConnection
}
}
+ println!("new_inner::step s: {}, w: {}",
+ sync::Arc::strong_count(&shared),
+ sync::Arc::weak_count(&shared));
+
if shared.config.max_lifetime().is_some() || shared.config.idle_timeout().is_some() {
let s = shared.clone();
shared.thread_pool
.run_at_fixed_rate(Duration::seconds(reaper_rate), move || reap_connections(&s));
}
+ println!("new_inner::step s: {}, w: {}",
+ sync::Arc::strong_count(&shared),
+ sync::Arc::weak_count(&shared));
+
Ok(Pool(shared))
}
diff --git a/src/task.rs b/src/task.rs
index 51c5841..b99e9e1 100644
--- a/src/task.rs
+++ b/src/task.rs
@@ -75,8 +75,11 @@ pub struct ScheduledThreadPool {
impl Drop for ScheduledThreadPool {
fn drop(&mut self) {
+ println!("pool dropping");
self.shared.inner.lock().unwrap().shutdown = true;
+ println!("pool notifying");
self.shared.cvar.notify_all();
+ println!("pool notified");
}
}
@@ -165,12 +168,14 @@ impl Worker {
}
fn run(&mut self) {
+ println!("pool worker starting");
loop {
match self.get_job() {
Some(job) => self.run_job(job),
None => break,
}
}
+ println!("pool worker stopping");
}
fn get_job(&self) -> Option<Job> {
@@ -190,6 +195,8 @@ impl Worker {
Some(e) => Need::WaitTimeout(e.time - now),
};
+ println!("pool worker waiting");
+
inner = match need {
Need::Wait => self.shared.cvar.wait(inner).unwrap(),
Need::WaitTimeout(t) => {
@@ -206,6 +213,7 @@ impl Worker {
}
fn run_job(&self, job: Job) {
+ println!("pool worker got job");
match job.type_ {
JobType::Once(f) => f.invoke(()),
JobType::FixedRate { mut f, rate } => {
I'm not very experienced in rust yet, so I'm sorry for potential misunderstanding ;)
I ran into an issue where I'd like to abstract returning a connection from a pool into a function. Basically the original code in Iron request handler is:
fn front_page_handler(req: &mut Request) -> IronResult<Response> {
let pool = req.get::<Read<Database>>().ok().expect("database component not initialised");
let connection = pool.get().unwrap();
...
and I wanted to make the first two lines just let connection = get_pool_connection(req);
. This however is not possible unless pool
is returned along with the connection so it can live for the same time.
I was hoping this could be improved by storing an Rc<Pool>
somewhere in the connection itself. Or maybe there are other ways to allow a function like this to exist:
fn get_pool_connection(req: &mut Request) -> PooledConnection<T> {
let pool = req.get::<Read<Database>>().ok().expect("database component not initialised");
pool.get().unwrap()
}
Related SO with the question and answer here: https://stackoverflow.com/questions/30252054/refactoring-messes-up-mutable-borrow-why
This issue was automatically generated. Feel free to close without ceremony if
you do not agree with re-licensing or if it is not possible for other reasons.
Respond to @cmr with any questions or concerns, or pop over to
#rust-offtopic
on IRC to discuss.
You're receiving this because someone (perhaps the project maintainer)
published a crates.io package with the license as "MIT" xor "Apache-2.0" and
the repository field pointing here.
TL;DR the Rust ecosystem is largely Apache-2.0. Being available under that
license is good for interoperation. The MIT license as an add-on can be nice
for GPLv2 projects to use your code.
The MIT license requires reproducing countless copies of the same copyright
header with different names in the copyright field, for every MIT library in
use. The Apache license does not have this drawback. However, this is not the
primary motivation for me creating these issues. The Apache license also has
protections from patent trolls and an explicit contribution licensing clause.
However, the Apache license is incompatible with GPLv2. This is why Rust is
dual-licensed as MIT/Apache (the "primary" license being Apache, MIT only for
GPLv2 compat), and doing so would be wise for this project. This also makes
this crate suitable for inclusion and unrestricted sharing in the Rust
standard distribution and other projects using dual MIT/Apache, such as my
personal ulterior motive, the Robigalia project.
Some ask, "Does this really apply to binary redistributions? Does MIT really
require reproducing the whole thing?" I'm not a lawyer, and I can't give legal
advice, but some Google Android apps include open source attributions using
this interpretation. Others also agree with
it.
But, again, the copyright notice redistribution is not the primary motivation
for the dual-licensing. It's stronger protections to licensees and better
interoperation with the wider Rust ecosystem.
To do this, get explicit approval from each contributor of copyrightable work
(as not all contributions qualify for copyright, due to not being a "creative
work", e.g. a typo fix) and then add the following to your README:
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
additional terms or conditions.
and in your license headers, if you have them, use the following boilerplate
(based on that used in Rust):
// Copyright 2016 r2d2 Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
It's commonly asked whether license headers are required. I'm not comfortable
making an official recommendation either way, but the Apache license
recommends it in their appendix on how to use the license.
Be sure to add the relevant LICENSE-{MIT,APACHE}
files. You can copy these
from the Rust repo for a plain-text
version.
And don't forget to update the license
metadata in your Cargo.toml
to:
license = "MIT OR Apache-2.0"
I'll be going through projects which agree to be relicensed and have approval
by the necessary contributors and doing this changes, so feel free to leave
the heavy lifting to me!
To agree to relicensing, comment with :
I license past and future contributions under the dual MIT/Apache-2.0 license, allowing licensees to chose either at their option.
Or, if you're a contributor, you can check the box in this repo next to your
name. My scripts will pick this exact phrase up and check your checkbox, but
I'll come through and manually review this issue later as well.
It would be very nice if r2d2 would provide some mocking abilities to be able to test that database-accessing code will perform the things expected of it.
I'd like to be able to have an integration-style test where I init the connection, start a transaction, set some state for the DB, call the tests, and inspect the state of the DB afterwards, and finally, cancel the transaction. However, as my application (that uses Diesel + r2d2 + Rocket) uses r2d2 pool, achieving this isn't easy, since r2d2 manages all the connections, and having a transaction persist from before to after the test code doesn't work.
In the end, I ended up wrapping the r2d2 pool into a newtype, and having the implementation of the API of the newtype switched for a mock with #[cfg(test)]
. Lately I'm embracing even more problems since I'm splitting my code into a library and a binary crate, and #[cfg(test)]
applies only for the bottom crate being compiled.
If r2d2 had a "generic test connection manager" where instead of a pool, there would be only one prepared connection that is passed to it, writing tests like this would be a lot easier while keeping the same type interface. What do you think?
Hi,
I have a issue when i use r2d2_postgres.
i create a struct named App, and it have a field pool, type is Arcr2d2::Pool, when i impl trait named
Key, i got a error. What is it mean?
src/main.rs:63:5: 63:53 error: the trait bound `r2d2_postgres::PostgresConnectionManager: r2d2::ManageConnection` is not satisfied [E0277]
src/main.rs:63 pool: Arc<r2d2::Pool<PostgresConnectionManager>>,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:63:5: 63:53 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:63:5: 63:53 note: required by `r2d2::Pool`
src/main.rs:66:6: 66:9 error: the trait bound `r2d2_postgres::PostgresConnectionManager: r2d2::ManageConnection` is not satisfied [E0277]
src/main.rs:66 impl Key for App { type Value = App; }
^~~
src/main.rs:66:6: 66:9 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:66:6: 66:9 note: required because of the requirements on the impl of `std::marker::Reflect` for `alloc::arc::ArcInner<r2d2::SharedPool<r2d2_postgres::PostgresConnectionManager>>`
src/main.rs:66:6: 66:9 note: required because of the requirements on the impl of `std::marker::Reflect` for `std::marker::PhantomData<alloc::arc::ArcInner<r2d2::SharedPool<r2d2_postgres::PostgresConnectionManager>>>`
src/main.rs:66:6: 66:9 note: required because of the requirements on the impl of `std::marker::Reflect` for `std::ptr::Shared<alloc::arc::ArcInner<r2d2::SharedPool<r2d2_postgres::PostgresConnectionManager>>>`
src/main.rs:66:6: 66:9 note: required because of the requirements on the impl of `std::marker::Reflect` for `std::sync::Arc<r2d2::SharedPool<r2d2_postgres::PostgresConnectionManager>>`
src/main.rs:66:6: 66:9 note: required because of the requirements on the impl of `std::marker::Reflect` for `r2d2::Pool<r2d2_postgres::PostgresConnectionManager>`
src/main.rs:66:6: 66:9 note: required because of the requirements on the impl of `std::marker::Reflect` for `alloc::arc::ArcInner<r2d2::Pool<r2d2_postgres::PostgresConnectionManager>>`
src/main.rs:66:6: 66:9 note: required because of the requirements on the impl of `std::marker::Reflect` for `std::marker::PhantomData<alloc::arc::ArcInner<r2d2::Pool<r2d2_postgres::PostgresConnectionManager>>>`
src/main.rs:66:6: 66:9 note: required because of the requirements on the impl of `std::marker::Reflect` for `std::ptr::Shared<alloc::arc::ArcInner<r2d2::Pool<r2d2_postgres::PostgresConnectionManager>>>`
src/main.rs:66:6: 66:9 note: required because of the requirements on the impl of `std::marker::Reflect` for `std::sync::Arc<r2d2::Pool<r2d2_postgres::PostgresConnectionManager>>`
src/main.rs:66:6: 66:9 note: required because of the requirements on the impl of `std::marker::Reflect` for `App`
src/main.rs:66:6: 66:9 note: required by `iron::typemap::Key`
src/main.rs:89:5: 91:6 error: the trait bound `r2d2_postgres::PostgresConnectionManager: r2d2::ManageConnection` is not satisfied [E0277]
src/main.rs:89 pub fn conn(&self) -> Result<Connection, Error> {
src/main.rs:90 Ok(try!(self.pool.get()))
src/main.rs:91 }
src/main.rs:89:5: 91:6 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:89:5: 91:6 note: required by `r2d2::PooledConnection`
The pool should be able to deal with panics coming from the PoolManager
and ErrorHandler
methods.
The easiest solution would be to poison the pool like Mutex
, but we could do better since the lock is never held during calls to user code. It's not clear if it's worth the effort though.
Sorry to ask here, I'm not sure where a better place to ask is (there's no IRC or gitter link in the README)... crates.io has this function which I am completely stumped on how to migrate to 0.8.0. Any help would be appreciated.
pub fn diesel_pool(
url: &str,
config: r2d2::Config<PgConnection, r2d2_diesel::Error>,
) -> DieselPool {
let mut url = Url::parse(url).expect("Invalid database URL");
if env::var("HEROKU").is_ok() && !url.query_pairs().any(|(k, _)| k == "sslmode") {
url.query_pairs_mut().append_pair("sslmode", "require");
}
let manager = ConnectionManager::new(url.into_string());
r2d2::Pool::new(config, manager).unwrap()
}
Almost library support async operation. So please support async.
Ref: https://github.com/khuey/bb8
error[E0277]: the type (dyn r2d2::HandleError<postgres::Error> + 'static)
may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> src/lib.rs:81:5
|
81 | catch_unwind(|| {
| ^^^^^^^^^^^^ (dyn r2d2::HandleError<postgres::Error> + 'static)
may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
= help: within *mut *mut r2d2::Pool<r2d2_postgres::PostgresConnectionManager>
, the trait std::panic::RefUnwindSafe
is not implemented for (dyn r2d2::HandleError<postgres::Error> + 'static)
= note: required because it appears within the type std::marker::PhantomData<(dyn r2d2::HandleError<postgres::Error> + 'static)>
= note: required because it appears within the type std::ptr::Unique<(dyn r2d2::HandleError<postgres::Error> + 'static)>
= note: required because it appears within the type std::boxed::Box<(dyn r2d2::HandleError<postgres::Error> + 'static)>
= note: required because it appears within the type r2d2::config::Config<postgres::Connection, postgres::Error>
= note: required because it appears within the type r2d2::SharedPool<r2d2_postgres::PostgresConnectionManager>
= note: required because it appears within the type std::marker::PhantomData<r2d2::SharedPool<r2d2_postgres::PostgresConnectionManager>>
= note: required because it appears within the type std::sync::Arc<r2d2::SharedPool<r2d2_postgres::PostgresConnectionManager>>
= note: required because it appears within the type r2d2::Pool<r2d2_postgres::PostgresConnectionManager>
= note: required because it appears within the type *mut r2d2::Pool<r2d2_postgres::PostgresConnectionManager>
= note: required because it appears within the type *mut *mut r2d2::Pool<r2d2_postgres::PostgresConnectionManager>
= note: required because of the requirements on the impl of std::panic::UnwindSafe
for &*mut *mut r2d2::Pool<r2d2_postgres::PostgresConnectionManager>
= note: required because it appears within the type [closure@src/lib.rs:81:18: 90:6 c_sqlurl:&*const i8, c_size:&u16, out:&*mut *mut r2d2::Pool<r2d2_postgres::PostgresConnectionManager>]
note: required by catch_unwind
--> src/lib.rs:491:1
|
491| fn catch_unwind(f: F) -> CBoxResult where F: FnOnce() -> CBoxResult + UnwindSafe {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Coming to the repo it's completely undiscoverable what this library actually does. There is no information in the README at all nor is there even a description of the repo on GitHub.
Please add some information, atleast the basics would be appreciated.
Using the r2d2-diesel crate,
let db_manager = ConnectionManager::<MysqlConnection>::new(db_url);
let db_pool = r2d2::Pool::new(db_config, db_manager).unwrap();
results in
error[E0277]: the trait bound `diesel::mysql::MysqlConnection: diesel::connection::Connection` is not satisfied
--> src\main.rs:33:5
|
33 | db_pool: r2d2::Pool<ConnectionManager<MysqlConnection>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `diesel::connection::Connection` is not implem
ented for `diesel::mysql::MysqlConnection`
|
= note: required because of the requirements on the impl of `r2d2::ManageConnection` for `r2d2_diesel::ConnectionManag
er<diesel::mysql::MysqlConnection>`
= note: required by `r2d2::Pool`
error: aborting due to previous error
Obviously MysqlConnection is satisfied. Kinda confused.
The way errors are reported appears to be racey/inconsistent when a connection cannot be obtained from the pool. Sometimes you will just get Error(None)
, whilst other times you will get an actual error message from the connection manager. I assume this is because another thread happens to take the error message before the original thread gets to it.
I can see why there might be reasons to not want to synchronously establish the connection from the thread that could not find an available connection in the pool (eg. so that its request could be fullfilled sooner if another connection is returned to the pool more quickly) but I think when a connection cannot be established, it should behave as though it synchronously attempted to establish the new connection (guaranteeing a 1:1 relationship between failed requests and errors).
It would also be useful to distinguish failures due to hitting the "max_size" on the pool, from connection failures - at the moment you also (IIUC) get an Error(None)
if the pool is maxed out.
My application create a pool at startup to connect to one API and right now this API is down so my application can't start. I don't really need the API to be up when I start my application, only when I get a request for it.
Is there a way to do that?
Because I did not see information about cassandra on the list, so if i want to build like Sql, is there a related way?
So I noticed if I use the wrong username, password, port, etc... then I will not get an error back until the connection timeout has occurred. Is the canonical way of dealing with this to use something like the postgres crate directly and make sure the credentials are correct before setting up a pool?
All types which are Sync
should also implement RefUnwindSafe
as the former implies the latter. Currently the Pool
type is not RefUnwindSafe
(through no fault of its own) because it stores a Condvar
, and Condvar
is also (incorrectly) missing a RefUnwindSafe
implementation in the rust standard library.
However, r2d2
could explicitly provide this implementation.
What exactly is r2d2_foodb::FooConnectionManager
in the example and is there a source code of it somewhere? I tried using something like this:
let config = r2d2::Config::default();
let manager = r2d2::ConnectionManager::new("postgres://postgres:postgres@localhost/test");
let error_handler = Box::new(r2d2::LoggingErrorHandler);
let pool = Arc::new(r2d2::Pool::new(config, manager, error_handler).unwrap());
but it throws an error
src/main.rs:125:16: 125:44 error: the value of the associated type `Error` (from the trait `r2d2::ConnectionManager`) must be specified [E0191]
src/main.rs:125 let manager = r2d2::ConnectionManager::new(DB_URI);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:125:16: 125:44 error: the value of the associated type `Connection` (from the trait `r2d2::ConnectionManager`) must be specified [E0191]
src/main.rs:125 let manager = r2d2::ConnectionManager::new(DB_URI);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:125:16: 125:44 error: no associated item named `new` found for type `r2d2::ConnectionManager` in the current scope
src/main.rs:125 let manager = r2d2::ConnectionManager::new(DB_URI);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 3 previous errors
Currently PooledConnection
unconditionally returns the connection to the pool on drop. Since a panic could happen at any time, we can't assume that the connection will be in a good state if it's being dropped as the result of unwinding. r2d2 should either check thread::panicking
before checking the connection back into the pool, or should have a T: UnwindSafe
constraint on the connection.
Perhaps this more of a upstream issue, but I have found that when using a binary on a different machine same OS and same packages are installed and I think even the hardware is the same, I get the error
Result::unwrap()` on an `Err` value: InitializationError(None)
I realize that this is error is thrown when the Pool::new get unwrapped, but I am actually blaming the builder object namely
let config = r2d2::config::Builder::new().pool_size(20).build();
println!("{:?}", config);
config will not print anything which is weird since Builder implements the debug trait. Any thoughts on how to resolve this?
New pool test after 41b268c
Test code same as
sfackler/r2d2-postgres#12
Expected: After idle_timeout pool redused to 2 connections
Reality: Pool reduced after max_lifetime to 3 connections and not renew used connections
Test query
SELECT a.pid, a.backend_start, a.query_start FROM pg_stat_activity a WHERE a.datid IS NOT NULL ORDER BY a.pid;
At begin
15490 / 2018-09-28 09:19:49.329870 / 2018-09-28 09:19:49.332444
15491 / 2018-09-28 09:19:49.330124 / 2018-09-28 09:19:52.336268
15492 / 2018-09-28 09:19:49.332135 / 2018-09-28 09:19:49.334726
15493 / 2018-09-28 09:19:49.333422 / 2018-09-28 09:19:49.334606
After around 1min (max_lifetime = Duration::from_secs(60))
15491 / 2018-09-28 09:19:49.330124 / 2018-09-28 09:20:19.360589
15620 / 2018-09-28 09:20:19.349820 /
15621 / 2018-09-28 09:20:19.333852 /
After 2min
15491 / 2018-09-28 09:19:49.330124 / 2018-09-28 09:21:21.400551
19526 / 2018-09-28 09:21:19.334243 /
19527 / 2018-09-28 09:21:19.341293 /
So, what happens if we use two connections instead of one?
loop {
println!("LOOP");
let db1 = pool.get();
println!("1/{} {:?}", db_max, db1.is_ok());
let db2 = pool.get();
println!("2/{} {:?}", db_max, db2.is_ok());
std::thread::sleep(Duration::from_secs(1));
}
After few minutes
435 / 2018-09-28 09:59:27.376183 /
436 / 2018-09-28 09:59:27.377418 /
32146 / 2018-09-28 09:53:57.376039 / 2018-09-28 09:59:26.856081
32147 / 2018-09-28 09:53:57.376298 / 2018-09-28 09:59:26.856594
After more time used connections renew, but pool size not reduced
1447 / 2018-09-28 10:12:57.381760
1448 / 2018-09-28 10:12:57.381966
32146 / 2018-09-28 09:53:57.376039 / 2018-09-28 10:13:07.963099
32147 / 2018-09-28 09:53:57.376298 / 2018-09-28 10:13:07.963322
Same behavior on v0.8.2
error: failed to run custom build command for miniz-sys v0.1.11
process didn't exit successfully: D:\Rust\Rd2dTest\target\debug\build\miniz-sys-d13c924666e24cd6\build-script-build
(exit code: 101)
--- stdout
TARGET = Some("x86_64-pc-windows-gnu")
OPT_LEVEL = Some("0")
HOST = Some("x86_64-pc-windows-gnu")
CC_x86_64-pc-windows-gnu = None
CC_x86_64_pc_windows_gnu = None
HOST_CC = None
CC = None
CFLAGS_x86_64-pc-windows-gnu = None
CFLAGS_x86_64_pc_windows_gnu = None
HOST_CFLAGS = None
CFLAGS = None
CRATE_CC_NO_DEFAULTS = None
DEBUG = Some("true")
CARGO_CFG_TARGET_FEATURE = Some("fxsr,mmx,sse,sse2")
running: "gcc.exe" "-O0" "-ffunction-sections" "-fdata-sections" "-g" "-fno-omit-frame-pointer" "-m64" "-DMINIZ_NO_STDIO" "-DMINIZ_NO_ARCHIVE_APIS" "-DMINIZ_NO_ARCHIVE_WRITING_APIS" "-DMINIZ_NO_TIME" "-DMINIZ_NO_ZLIB_COMPATIBLE_NAMES" "-o" "D:\Rust\Rd2dTest\target\debug\build\miniz-sys-935e563594ae4ae2\out\miniz.o" "-c" "miniz.c"
cargo:warning=cc1.exe: sorry, unimplemented: 64-bit mode not compiled in
exit code: 1
--- stderr
thread 'main' panicked at '
Internal error occurred: Command "gcc.exe" "-O0" "-ffunction-sections" "-fdata-sections" "-g" "-fno-omit-frame-pointer" "-m64" "-DMINIZ_NO_STDIO" "-DMINIZ_NO_ARCHIVE_APIS" "-DMINIZ_NO_ARCHIVE_WRITING_APIS" "-DMINIZ_NO_TIME" "-DMINIZ_NO_ZLIB_COMPATIBLE_NAMES" "-o" "D:\Rust\Rd2dTest\target\debug\build\miniz-sys-935e563594ae4ae2\out\miniz.o" "-c" "miniz.c" with args "gcc.exe" did not execute successfully (status code exit code: 1).
', C:\Users\xiujie.zhu.cargo\registry\src\github.com-1ecc6299db9ec823\cc-1.0.35\src\lib.rs:2398:5
stack backtrace:
0: std::sys_common::backtrace::_print
at src\libstd\sys\windows\backtrace/mod.rs:95
at src\libstd\sys\windows\backtrace/mod.rs:82
at src\libstd\sys_common/backtrace.rs:71
1: std::panicking::default_hook::{{closure}}
at src\libstd\sys_common/backtrace.rs:59
at src\libstd/panicking.rs:197
2: std::panicking::default_hook
at src\libstd/panicking.rs:211
3: std::panicking::rust_panic_with_hook
at src\libstd/panicking.rs:474
4: std::panicking::continue_panic_fmt
at src\libstd/panicking.rs:381
5: std::panicking::begin_panic_fmt
at src\libstd/panicking.rs:336
6: cc::fail
at C:\Users\xiujie.zhu.cargo\registry\src\github.com-1ecc6299db9ec823\cc-1.0.35\src/lib.rs:2398
7: cc::Build::compile
at C:\Users\xiujie.zhu.cargo\registry\src\github.com-1ecc6299db9ec823\cc-1.0.35\src/lib.rs:951
8: build_script_build::main
at .\build.rs:23
9: std::rt::lang_start::{{closure}}
at /rustc/474e7a6486758ea6fc761893b1a49cd9076fb0ab\src\libstd/rt.rs:64
10: std::panicking::try::do_call
at src\libstd/rt.rs:49
at src\libstd/panicking.rs:293
11: _rust_maybe_catch_panic
at src\libpanic_unwind/lib.rs:87
12: std::rt::lang_start_internal
at src\libstd/panicking.rs:272
at src\libstd/panic.rs:388
at src\libstd/rt.rs:48
13: std::rt::lang_start
at /rustc/474e7a6486758ea6fc761893b1a49cd9076fb0ab\src\libstd/rt.rs:64
14: main
15: _tmainCRTStartup
16: mainCRTStartup
17: unit_addrs_search
18: unit_addrs_search
Can the example in the README include how to set that we don't have to have more than X number of connection open ?
Right now all one gets is InitializationError(())
which gives very little information about what could have gone wrong, even if there is an informative error from the connection-opening function.
Hi.
min_idle
and max_lifetime
or idle_timeout
are how prioritize?
Q: Set to 10 min_idle
and set 10 secs to max_lifetime
and then after 10 secs...
A1: kill 10 connections, and then create new connections for keep min_idle
A2: ignore max_lifetime
for keep min_idle
Q: Set to 10 min_idle
and set 10 secs to idle_timeout
and then after 10 secs all connections are idle...
A1: kill 10 connections, and then create new connections for keep min_idle
A2: ignore idle_timeout
for keep min_idle
Which answer is correct? Or another?
Hello,
What is the expected behaviour for an has_broken
implementation ?
Should the connection be dropped explicitly when broken ?
Should the connection be considered broken when:
I am trying to create an adaptor for SQLite:
https://github.com/gwenn/r2d2-sqlite.
Regards
loop {
let conn = pool.get().unwrap();
// if conn is reuse connection, call prepare_cached just return already statment,
// if conn is new prepare the statment and return;
// Is Right ?
let stmp = conn.prepare_cached("sql").unwrap();
stmt.query(&[]).unwrap();
}
I'm getting some very strange behavior. My code works fine on a particular commit (pool used thousands of times, reclaims resources as connections fall out of scope). Then when I make the minimal changes necessary to upgrade to rust nightly (from 6-14 to 8-10) the (nearly) same code runs out of database connections quickly, even if I jack up the number of connections from my default 5 to any larger number, it will eventually consume them all.
The only changes I made to cause this to happen are:
serde
, serde_macros
, serde_json
(from 7 to 8)regex_macros
(0.1.37 to 0.1.38)maud_macros
(some commit to 0.9.2)bincode
(0.5 to 0.6)handlebars
(0.17 to 0.20)Notice, no upgrades to r2d2
, postgres
, or r2d2-postgres
I've spent about 6 hours on this today. Tomorrow I'll see if a simple example exhibits the same behavior, then try bisecting rust-nightly versions.
Failure in a destructor apparently leaks the entire stack frame right now, so we have to indirect through another function. rust-lang/rust#14875
In version 0.8.x, there's no longer an initialization_fail_fast
option in the configuration for a Pool. Are connections always initialized synchronously now, or is there no way to achieve this behavior anymore?
I'm using r2d2 in a (closed source) project for a customer, and it seems to try to incorrectly free a pointer.
stdout is: *** Error in `/home/vincent/coolerbot/release/bot': free(): invalid pointer: 0x00007f1e2c076c60 ***
Debian 3.16.59-1
rustc 1.32.0
r2d2 .8.3
pg-sys 0.4.6
diesel 1.4.1
postgres:
postgresql-10/jessie-pgdg,now 10.6-1.pgdg80+1 amd64 [installed]
postgresql-client-10/jessie-pgdg,now 10.6-1.pgdg80+1 amd64 [installed,automatic]
postgresql-client-common/jessie-pgdg,now 199.pgdg80+1 all [installed,automatic]
postgresql-common/jessie-pgdg,now 199.pgdg80+1 all [installed,automatic]
r2d2 pool is created with:
use diesel::pg::PgConnection;
use diesel::r2d2::ConnectionManager;
use r2d2::Pool;
#[derive(Clone)]
pub struct Connection {
pub(crate) conn: Pool<ConnectionManager<PgConnection>>,
}
impl Connection {
pub fn connect(url: &str) -> Result<Connection> {
let conn = Pool::new(ConnectionManager::new(url))?;
Ok(Connection { conn })
}
}
fn main() {
let conn = Connection::connect(&config.database_url).expect("Could not connect to database");
}
gdb backtrace:
#0 0x00007f1e34cdb067 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1 0x00007f1e34cdc448 in __GI_abort () at abort.c:89
#2 0x00007f1e34d191b4 in __libc_message (do_abort=do_abort@entry=1, fmt=fmt@entry=0x7f1e34e0e210 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:175
#3 0x00007f1e34d1e98e in malloc_printerr (action=1, str=0x7f1e34e0a326 "free(): invalid pointer", ptr=<optimized out>) at malloc.c:4996
#4 0x00007f1e34d1f696 in _int_free (av=<optimized out>, p=<optimized out>, have_lock=0) at malloc.c:3840
#5 0x00007f1e3589c4a0 in ?? () from /usr/lib/x86_64-linux-gnu/libpq.so.5
#6 0x000055b45221eeb0 in r2d2::CustomizeConnection::on_release::hf8217cbd7c8ed8c5 ()
#7 0x000055b45221e2de in r2d2::drop_conns::h3df3ef3e77ab5c7f ()
#8 0x000055b45221ecf5 in r2d2::reap_connections::h84ed5f0047adfb7f ()
#9 0x000055b452281c31 in scheduled_thread_pool::Worker::run_job::h3b008cb21134a03b ()
#10 0x000055b452282311 in std::panicking::try::do_call::hef918f7ba5b2651f ()
#11 0x000055b4522a609a in __rust_maybe_catch_panic () at src/libpanic_unwind/lib.rs:102
#12 0x000055b4522819a9 in scheduled_thread_pool::Worker::run::h1861b2dabd2cd6ed ()
#13 0x000055b4522839f4 in std::sys_common::backtrace::__rust_begin_short_backtrace::h0ebd79a6a13e4f1f ()
#14 0x000055b4522a609a in __rust_maybe_catch_panic () at src/libpanic_unwind/lib.rs:102
#15 0x000055b45228362c in _$LT$F$u20$as$u20$alloc..boxed..FnBox$LT$A$GT$$GT$::call_box::h8e8ed42246ded358 ()
#16 0x000055b452299cfe in call_once<(),()> () at /rustc/9fda7c2237db910e41d6a712e9a2139b352e558b/src/liballoc/boxed.rs:683
#17 start_thread () at src/libstd/sys_common/thread.rs:24
#18 std::sys::unix::thread::Thread::new::thread_start::hca8e72c41fa9d291 () at src/libstd/sys/unix/thread.rs:90
#19 0x00007f1e3526f064 in start_thread (arg=0x7f1e31f23700) at pthread_create.c:309
#20 0x00007f1e34d8e62d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
0.8.0 included significant breaking changes which have non-obvious migration paths. There is no documentation anywhere in this project of what changed, or how someone should migrate to the latest version. http://keepachangelog.com/ is a reasonable place to start if you're looking for a format to use.
I use postgres with rust for backend but both diesel and rust-postgres are synchronous. I want to use tokio-postgres but there is no connection pool for it. I think it would be good if there was a adapter for it.
When no servers are running, I get the following error:-
thread 'r2d2-worker-0' has overflowed its stack
fatal runtime error: stack overflow
This means that if I temporarily take the database down for maintenance or it goes down for some reason, my app crashes even when it could continue running using cached data.
I have two cases where I would like to catch and handle the fact that my DB is down.
In both cases, as far as I can tell, there is no way for me to catch the error and gracefully handle it.
let manager = PostgresConnectionManager::new(config.db_uri.unwrap(), TlsMode::None).unwrap();
let pool = r2d2::Pool::new(manager).unwrap();
...
let conn = pconn.get().unwrap();
let request_query = format!("SELECT ... LIMIT 1;");
let request_rcon = conn.query(&request_query, &[]);
if request_rcon.is_err() {
...
}
I would expect the conn.query
to return an error that I can catch when there is a connection issue but it doesn't. In both cases, it spins dumping the following to console:
ERROR r2d2 > IO error: Connection refused (os error 111)
ERROR r2d2 > IO error: Connection refused (os error 111)
ERROR r2d2 > IO error: Connection refused (os error 111)
ERROR r2d2 > IO error: Connection refused (os error 111)
ERROR r2d2 > IO error: Connection refused (os error 111)
I would love a way where I can gracefully catch and handle this error, and not have the application hang.
Hello,
I have a connection pool, implemented like in writing a github webhook in rust pt 1,
code at this link. I'm not sure what I am doing wrong, but when I call
let conn: db::DbConnection = db_pool.get().chain_err(|| "Could not connect to DB")?;
let returned_user: User = diesel::insert(&user_data)
.into(schema::users::table)
.get_result(&conn)
.chain_err(|| "Error inserting user")?;
I get the following error message:
error[E0277]: the trait bound `r2d2::PooledConnection<r2d2_diesel::ConnectionManager<diesel::pg::PgConnection>>: diesel::Connection` is not satisfied
--> src/app/mod.rs:45:6
|
45 | .get_result(&conn)
| ^^^^^^^^^^ the trait `diesel::Connection` is not implemented for `r2d2::PooledConnection<r2d2_diesel::ConnectionManager<diesel::pg::PgConnection>>`
diesel::pg::PgConnection
implements diesel::Connection
. Is there any way to convert from a pooled connection to a regular connection? It doesn't look like it from the docs.
My workaround is to call the establish_connection
method (from the same link as above), which returns a PgConnection
. However, grabbing a pooled connection or a regular connection depending on whether we're inserting or querying seems really fishy.
Am I doing something wrong? Thank you!
I don't see anywhere how to set the pool so that it keeps X number of connections open at any moments to keep the pool warm in case of burst ?
There are many Databases(300 Hundreds of...) need be connected...
There is one ScheduledThreadPool
for one Pool
, and one ScheduledThreadPool
will start 3 threads by default, then there will be about 900 threads started...
It is huge...
So, If the ScheduledThreadPool
could be shared between Pool, then that would be great
can see this example project.
https://github.com/damody/r2d2mysql
Hi!
I'm trying to compile code:
struct Foo {
pool: r2d2::Pool<r2d2_mysql::MysqlConnectionManager>,
}
which gives me error
src/main.rs:65:5: 65:39 error: the trait bound `r2d2_mysql::MysqlConnectionManager: r2d2::ManageConnection` is not satisfied [E0277]
src/main.rs:65 pool: Pool<MysqlConnectionManager>,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:65:5: 65:39 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:65:5: 65:39 note: required by `r2d2::Pool`
My Cargo.toml
[dependencies]
mysql = "*"
r2d2 = "*"
r2d2_mysql = "*"
rustc-serialize = "*"
If I try to use https://github.com/sfackler/r2d2-postgres and replace code with
struct Foo {
pool: r2d2::Pool<r2d2_postgres::PostgresConnectionManager>,
}
everything compiles.
I can't see real differences in this two cargos. Both have
impl r2d2::ManageConnection for PostgresConnectionManager {
impl r2d2::ManageConnection for MysqlConnectionManager {
I would appreciate any help. Thanks
Hi, I noticed the tutorial for this piece of Diesel rs is a bit lackluster. I am attempting to use r2d2 for its connection pool capabilities, but I have noticed it's kind of hard to get started with the example given.
use std::thread;
extern crate r2d2;
extern crate r2d2_foodb; // Where does this come from?
fn main() {
let manager = r2d2_foodb::FooConnectionManager::new("localhost:1234"); // How does this get created?
let pool = r2d2::Pool::builder()
.max_size(15)
.build(manager)
.unwrap();
for _ in 0..20 {
let pool = pool.clone();
thread::spawn(move || {
let conn = pool.get().unwrap();
// use the connection
// it will be returned to the pool when it falls out of scope.
})
}
}
A cursory glance of this code still leaves me confused about how I may go about implementing this in my own system. For example, you import r2d2, but you use a connection manager for a separate r2d2 module, r2d2::foodb. After reading the source code for an example of how I can emulate this desired behavior, I have not been able to find what I am looking for. What is "foodb" and how does it get created as an external crate in this case?
Would you be willing to provide a more complete example of how this system works? It is still quite confusing in its current state.
I am currently trying to follow the rocket guide and implement a PgConnection in between api endpoints.
I have the following code, which was working before I updated to the newest version (I already adapted the function init_pool
following the changelog:
use diesel;
use diesel::prelude::*;
use r2d2_diesel::ConnectionManager;
use r2d2;
use types;
use time;
use std::ops::Deref;
use rocket::http::Status;
use rocket::request::{self, FromRequest};
use rocket::{Request, State, Outcome};
type Pool = r2d2::Pool<ConnectionManager<PgConnection>>;
pub struct DbConn(pub r2d2::PooledConnection<ConnectionManager<PgConnection>>);
impl<'a, 'r> FromRequest<'a, 'r> for DbConn {
type Error = ();
fn from_request(request: &'a Request<'r>) -> request::Outcome<DbConn, ()> {
let pool = request.guard::<State<Pool>>()?;
match pool.get() {
Ok(conn) => Outcome::Success(DbConn(conn)),
Err(_) => Outcome::Failure((Status::ServiceUnavailable, ()))
}
}
}
// For the convenience of using an &DbConn as an &SqliteConnection.
impl Deref for DbConn {
type Target = PgConnection;
fn deref(&self) -> &Self::Target {
&self.0
}
}
/**
Initialize a database pool
*/
pub fn init_pool() -> Pool {
let manager = ConnectionManager::<PgConnection>::new(DATABASE_URL);
let pool = r2d2::Pool::builder()
.build(manager).expect("db pool");
pool
}
However, I get the following weird error.
error[E0277]: the trait bound `diesel::PgConnection: diesel::connection::Connection` is not satisfied
--> src/database_worker.rs:26:19
|
26 | pub struct DbConn(pub r2d2::PooledConnection<ConnectionManager<PgConnection>>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `diesel::connection::Connection` is not implemented for `diesel::PgConnection`
|
= note: required because of the requirements on the impl of `r2d2::ManageConnection` for `r2d2_diesel::ConnectionManager<diesel::PgConnection>`
= note: required by `r2d2::PooledConnection`
error[E0277]: the trait bound `diesel::PgConnection: diesel::connection::Connection` is not satisfied
--> src/database_worker.rs:52:1
|
52 | / pub fn init_pool() -> Pool {
53 | | let manager = ConnectionManager::<PgConnection>::new(DATABASE_URL);
54 | | let pool = r2d2::Pool::builder()
55 | | .build(manager).expect("db pool");
56 | | pool
57 | | }
| |_^ the trait `diesel::connection::Connection` is not implemented for `diesel::PgConnection`
|
= note: required because of the requirements on the impl of `r2d2::ManageConnection` for `r2d2_diesel::ConnectionManager<diesel::PgConnection>`
= note: required by `r2d2::Pool`
error: aborting due to 2 previous errors
Any idea why I get these errors? This also seems to work for Sqlite, but not for PgConnection apparently.
Is there support for the mongodb from the mongodb driver?
Hi! I'm currently looking into using r2d2 for the connection pooling in the MongoDB Rust driver, and I've hit a minor roadblock with regards to be able to reset the connection pool in the case that the topology changes. Specifically, the driver uses a background thread per server to monitor the state of the topology, and if one of the monitoring threads loses the ability to connect to a server, then the driver needs to refresh all connections in the pool to establish the new topology. As far as I can tell, r2d2 doesn't currently provide this functionality publicly, although it appears that there's some functionality for it internally. Is there any chance that an ability to reset the connection pool could be made public in some form? I'd be happy to contribute a pull request if you're open to this idea but don't have the time to work on it.
Hey, I was wondering when I would know a connection is dropped? Is it when it leaves scope? I just need to configure some things using one of the pooled connections and then drop it right away before launching the backend.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.