Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

COS316, Assignment 5: Connection Pool

Due: April 13th at 11:00pm

Background

Establishing connections to a database often involves several expensive steps, such as creating a TCP channel with a database server, parsing a connection configuration string, and authenticating a user.

In practice, most applications (clients) use only one or a few different database connection configurations. This means that an application will repeatedly open and close many identical database connections.

Connection pooling is a way to avoid the overhead of reopening connections by reusing existing ones. A connection pool manages a set of established connections. When an application opens a connection to a database, the connection pool finds an existing connection and returns the connection to the application. When the application closes a connection, the connection pool adds the connection back to its list of available connections without actually closing the connection.

The diagram below illustrates a database connection pool at a high level:

DB Connection Pool

Objectives

The objective of this assignment is to implement a concurrent connection pool that manages a set of connections to a sql database. It should support the following APIs:

// Use this struct for storing pool related informationtypePoolstruct {
...
}
// A database connectiontypeConninterface {
Query(string) (*sql.Rows, error)
}
// NewPool creates a new connection pool with connections to a database.// It takes as input a function that actually establishes connections with// the underlying database.// Establishing a connection to a database might fail, in which case NewPool// propagates the error from the underlying driver to its caller.funcNewPool(newConnectionfunc() (Conn, error)) (*Pool, error)
// Open returns a connection from the connection pool. It should only return a// connection when the number of open (i.e., in-use) connections is less than the // maximum pool size. Open should block (i.e., not return) when the number of // open (i.e., in-use) connections exceeds the pool's maximum.// This function needs to be safe for concurrent use. When it is called by// multiple goroutines, it should return unique connections to each caller.func (p*Pool) Open() (*Conn)
// Close returns a connection back to the connection pool without actually closing// it with the underlying database.// When a connection is closed, using it has undefined behavior (i.e., applications// should not do it).// Close needs to return a connection to the pool it was originally allocated in.// If the connection does not belong to the pool (i.e., c was not allocated to p// originally), Close is a noop (i.e., no effects).// Closing a connection that's not open is a noop.func (p*Pool) Close(c*Conn)
// SetMaxConnections sets the maximum number of connections that a pool can// maintain. If it sets to a number m that is smaller than the number of currently// open connections, SetMaxConnections should block (i.e., not return) until the// number of open connections drops below m.func (p*Pool) SetMaxConnections(mint)
// GetMaxConnections returns the maximum number of connections that the pool can// maintain.func (p*Pool) GetMaxConnections() int

You might have noticed that this assignment is similar to Go's database/sql package that you used in the last assignment. In particular, the DB type implements a connection pool internally.

To actually establish connections with a database, the sql package relies on database drivers. For example, github.com/mattn/go-sqlite3 package that we used in precepts and last assignment implements a driver for sqlite3. It is the driver that handles communication with the underlying database. For example, when calling db.Conn() in the sql package to get a new connection, it internally calls another function in the go-sqlite3 package which actually establishes a connection with the database.

For this assignment, we are removing the complexity of a driver component from your connection pool implementation. Instead, you can just use the newConnection function (passed in as input to NewPool()) to acquire a new function with a underlying database. You can think of newConnection as a driver's implementation of establishing a new connection. The important note to keep in mind is that you cannot assume this newConnection function to be safe for concurrent use.

You should not block by busy looping (e.g., while condition {}). Instead use something like condition variables.

Concurrency

All the APIs should be safe for concurrent use. There will be multiple threads (goroutines) that use the APIs concurrently. You need to make sure that your connection pool behaves correctly. For example, calling Open() from multiple goroutines should return unique values to each caller, assuming there are enough available connections in the pool.

Moreover, if SetMaxConnection() sets the pool size to a number smaller than the number of currently open connections, Open() should also block (i.e., do not open new connections to clients) until the number of open connections drops below the new maximum.

Lazy Allocation

Connection pool should allocate connections lazily (i.e. on demand). This means your implementation should not fill the entire pool with connections when it is first created. Instead it should only open connections with the underlying database when a client calls Open(). In fact, for this assignment, increment the total number of connections in your pool by only one when necessary.

Example Application

An example client is provided in 'client/main.go'. You can use that client to see how your connection pool will be used and test your implementation.

The example application also implements a simple database driver. You can see an example of a newConnection function passed into the NewPool() function. For actual grading, we are using a more complex driver but the interface is exactly the same.

Resources

Go sync package: Go's mutex, condition variable, etc.

Embedded mutexes: You might want to check out using mutexes as an embedded field.

Submission & Grading

Your assignment will be automatically submitted every time you push your changes to your GitHub repo. Within a couple minutes of your submission, the autograder will make a comment on your commit listing the output of our testing suite when run against your code. Note that you will be graded only on your changes to the conn_pool package, and not on your changes to any other files, though you may modify any files you wish.

You may submit and receive feedback in this way as many times as you like, whenever you like, but a substantial lateness penalty will be applied to submissions past the deadline.

About

Database Connection Pool

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

COS316, Assignment 5: Connection Pool

Due: April 13th at 11:00pm

Background

Establishing connections to a database often involves several expensive steps, such as creating a TCP channel with a database server, parsing a connection configuration string, and authenticating a user.

In practice, most applications (clients) use only one or a few different database connection configurations. This means that an application will repeatedly open and close many identical database connections.

Connection pooling is a way to avoid the overhead of reopening connections by reusing existing ones. A connection pool manages a set of established connections. When an application opens a connection to a database, the connection pool finds an existing connection and returns the connection to the application. When the application closes a connection, the connection pool adds the connection back to its list of available connections without actually closing the connection.

The diagram below illustrates a database connection pool at a high level:

DB Connection Pool

Objectives

The objective of this assignment is to implement a concurrent connection pool that manages a set of connections to a sql database. It should support the following APIs:

// Use this struct for storing pool related informationtypePoolstruct {
...
}
// A database connectiontypeConninterface {
Query(string) (*sql.Rows, error)
}
// NewPool creates a new connection pool with connections to a database.// It takes as input a function that actually establishes connections with// the underlying database.// Establishing a connection to a database might fail, in which case NewPool// propagates the error from the underlying driver to its caller.funcNewPool(newConnectionfunc() (Conn, error)) (*Pool, error)
// Open returns a connection from the connection pool. It should only return a// connection when the number of open (i.e., in-use) connections is less than the // maximum pool size. Open should block (i.e., not return) when the number of // open (i.e., in-use) connections exceeds the pool's maximum.// This function needs to be safe for concurrent use. When it is called by// multiple goroutines, it should return unique connections to each caller.func (p*Pool) Open() (*Conn)
// Close returns a connection back to the connection pool without actually closing// it with the underlying database.// When a connection is closed, using it has undefined behavior (i.e., applications// should not do it).// Close needs to return a connection to the pool it was originally allocated in.// If the connection does not belong to the pool (i.e., c was not allocated to p// originally), Close is a noop (i.e., no effects).// Closing a connection that's not open is a noop.func (p*Pool) Close(c*Conn)
// SetMaxConnections sets the maximum number of connections that a pool can// maintain. If it sets to a number m that is smaller than the number of currently// open connections, SetMaxConnections should block (i.e., not return) until the// number of open connections drops below m.func (p*Pool) SetMaxConnections(mint)
// GetMaxConnections returns the maximum number of connections that the pool can// maintain.func (p*Pool) GetMaxConnections() int

You might have noticed that this assignment is similar to Go's database/sql package that you used in the last assignment. In particular, the DB type implements a connection pool internally.

To actually establish connections with a database, the sql package relies on database drivers. For example, github.com/mattn/go-sqlite3 package that we used in precepts and last assignment implements a driver for sqlite3. It is the driver that handles communication with the underlying database. For example, when calling db.Conn() in the sql package to get a new connection, it internally calls another function in the go-sqlite3 package which actually establishes a connection with the database.

For this assignment, we are removing the complexity of a driver component from your connection pool implementation. Instead, you can just use the newConnection function (passed in as input to NewPool()) to acquire a new function with a underlying database. You can think of newConnection as a driver's implementation of establishing a new connection. The important note to keep in mind is that you cannot assume this newConnection function to be safe for concurrent use.

You should not block by busy looping (e.g., while condition {}). Instead use something like condition variables.

Concurrency

All the APIs should be safe for concurrent use. There will be multiple threads (goroutines) that use the APIs concurrently. You need to make sure that your connection pool behaves correctly. For example, calling Open() from multiple goroutines should return unique values to each caller, assuming there are enough available connections in the pool.

Moreover, if SetMaxConnection() sets the pool size to a number smaller than the number of currently open connections, Open() should also block (i.e., do not open new connections to clients) until the number of open connections drops below the new maximum.

Lazy Allocation

Connection pool should allocate connections lazily (i.e. on demand). This means your implementation should not fill the entire pool with connections when it is first created. Instead it should only open connections with the underlying database when a client calls Open(). In fact, for this assignment, increment the total number of connections in your pool by only one when necessary.

Example Application

An example client is provided in 'client/main.go'. You can use that client to see how your connection pool will be used and test your implementation.

The example application also implements a simple database driver. You can see an example of a newConnection function passed into the NewPool() function. For actual grading, we are using a more complex driver but the interface is exactly the same.

Resources

Go sync package: Go's mutex, condition variable, etc.

Embedded mutexes: You might want to check out using mutexes as an embedded field.

Submission & Grading

Your assignment will be automatically submitted every time you push your changes to your GitHub repo. Within a couple minutes of your submission, the autograder will make a comment on your commit listing the output of our testing suite when run against your code. Note that you will be graded only on your changes to the conn_pool package, and not on your changes to any other files, though you may modify any files you wish.

You may submit and receive feedback in this way as many times as you like, whenever you like, but a substantial lateness penalty will be applied to submissions past the deadline.

About

Database Connection Pool

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

COS316, Assignment 5: Connection Pool

Due: April 13th at 11:00pm

Background

Establishing connections to a database often involves several expensive steps, such as creating a TCP channel with a database server, parsing a connection configuration string, and authenticating a user.

In practice, most applications (clients) use only one or a few different database connection configurations. This means that an application will repeatedly open and close many identical database connections.

Connection pooling is a way to avoid the overhead of reopening connections by reusing existing ones. A connection pool manages a set of established connections. When an application opens a connection to a database, the connection pool finds an existing connection and returns the connection to the application. When the application closes a connection, the connection pool adds the connection back to its list of available connections without actually closing the connection.

The diagram below illustrates a database connection pool at a high level:

DB Connection Pool

Objectives

The objective of this assignment is to implement a concurrent connection pool that manages a set of connections to a sql database. It should support the following APIs:

// Use this struct for storing pool related informationtypePoolstruct {
...
}
// A database connectiontypeConninterface {
Query(string) (*sql.Rows, error)
}
// NewPool creates a new connection pool with connections to a database.// It takes as input a function that actually establishes connections with// the underlying database.// Establishing a connection to a database might fail, in which case NewPool// propagates the error from the underlying driver to its caller.funcNewPool(newConnectionfunc() (Conn, error)) (*Pool, error)
// Open returns a connection from the connection pool. It should only return a// connection when the number of open (i.e., in-use) connections is less than the // maximum pool size. Open should block (i.e., not return) when the number of // open (i.e., in-use) connections exceeds the pool's maximum.// This function needs to be safe for concurrent use. When it is called by// multiple goroutines, it should return unique connections to each caller.func (p*Pool) Open() (*Conn)
// Close returns a connection back to the connection pool without actually closing// it with the underlying database.// When a connection is closed, using it has undefined behavior (i.e., applications// should not do it).// Close needs to return a connection to the pool it was originally allocated in.// If the connection does not belong to the pool (i.e., c was not allocated to p// originally), Close is a noop (i.e., no effects).// Closing a connection that's not open is a noop.func (p*Pool) Close(c*Conn)
// SetMaxConnections sets the maximum number of connections that a pool can// maintain. If it sets to a number m that is smaller than the number of currently// open connections, SetMaxConnections should block (i.e., not return) until the// number of open connections drops below m.func (p*Pool) SetMaxConnections(mint)
// GetMaxConnections returns the maximum number of connections that the pool can// maintain.func (p*Pool) GetMaxConnections() int

You might have noticed that this assignment is similar to Go's database/sql package that you used in the last assignment. In particular, the DB type implements a connection pool internally.

To actually establish connections with a database, the sql package relies on database drivers. For example, github.com/mattn/go-sqlite3 package that we used in precepts and last assignment implements a driver for sqlite3. It is the driver that handles communication with the underlying database. For example, when calling db.Conn() in the sql package to get a new connection, it internally calls another function in the go-sqlite3 package which actually establishes a connection with the database.

For this assignment, we are removing the complexity of a driver component from your connection pool implementation. Instead, you can just use the newConnection function (passed in as input to NewPool()) to acquire a new function with a underlying database. You can think of newConnection as a driver's implementation of establishing a new connection. The important note to keep in mind is that you cannot assume this newConnection function to be safe for concurrent use.

You should not block by busy looping (e.g., while condition {}). Instead use something like condition variables.

Concurrency

All the APIs should be safe for concurrent use. There will be multiple threads (goroutines) that use the APIs concurrently. You need to make sure that your connection pool behaves correctly. For example, calling Open() from multiple goroutines should return unique values to each caller, assuming there are enough available connections in the pool.

Moreover, if SetMaxConnection() sets the pool size to a number smaller than the number of currently open connections, Open() should also block (i.e., do not open new connections to clients) until the number of open connections drops below the new maximum.

Lazy Allocation

Connection pool should allocate connections lazily (i.e. on demand). This means your implementation should not fill the entire pool with connections when it is first created. Instead it should only open connections with the underlying database when a client calls Open(). In fact, for this assignment, increment the total number of connections in your pool by only one when necessary.

Example Application

An example client is provided in 'client/main.go'. You can use that client to see how your connection pool will be used and test your implementation.

The example application also implements a simple database driver. You can see an example of a newConnection function passed into the NewPool() function. For actual grading, we are using a more complex driver but the interface is exactly the same.

Resources

Go sync package: Go's mutex, condition variable, etc.

Embedded mutexes: You might want to check out using mutexes as an embedded field.

Submission & Grading

Your assignment will be automatically submitted every time you push your changes to your GitHub repo. Within a couple minutes of your submission, the autograder will make a comment on your commit listing the output of our testing suite when run against your code. Note that you will be graded only on your changes to the conn_pool package, and not on your changes to any other files, though you may modify any files you wish.

You may submit and receive feedback in this way as many times as you like, whenever you like, but a substantial lateness penalty will be applied to submissions past the deadline.

About

Database Connection Pool

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

COS316, Assignment 5: Connection Pool

Due: April 13th at 11:00pm

Background

Establishing connections to a database often involves several expensive steps, such as creating a TCP channel with a database server, parsing a connection configuration string, and authenticating a user.

In practice, most applications (clients) use only one or a few different database connection configurations. This means that an application will repeatedly open and close many identical database connections.

Connection pooling is a way to avoid the overhead of reopening connections by reusing existing ones. A connection pool manages a set of established connections. When an application opens a connection to a database, the connection pool finds an existing connection and returns the connection to the application. When the application closes a connection, the connection pool adds the connection back to its list of available connections without actually closing the connection.

The diagram below illustrates a database connection pool at a high level:

DB Connection Pool

Objectives

The objective of this assignment is to implement a concurrent connection pool that manages a set of connections to a sql database. It should support the following APIs:

// Use this struct for storing pool related informationtypePoolstruct {
...
}
// A database connectiontypeConninterface {
Query(string) (*sql.Rows, error)
}
// NewPool creates a new connection pool with connections to a database.// It takes as input a function that actually establishes connections with// the underlying database.// Establishing a connection to a database might fail, in which case NewPool// propagates the error from the underlying driver to its caller.funcNewPool(newConnectionfunc() (Conn, error)) (*Pool, error)
// Open returns a connection from the connection pool. It should only return a// connection when the number of open (i.e., in-use) connections is less than the // maximum pool size. Open should block (i.e., not return) when the number of // open (i.e., in-use) connections exceeds the pool's maximum.// This function needs to be safe for concurrent use. When it is called by// multiple goroutines, it should return unique connections to each caller.func (p*Pool) Open() (*Conn)
// Close returns a connection back to the connection pool without actually closing// it with the underlying database.// When a connection is closed, using it has undefined behavior (i.e., applications// should not do it).// Close needs to return a connection to the pool it was originally allocated in.// If the connection does not belong to the pool (i.e., c was not allocated to p// originally), Close is a noop (i.e., no effects).// Closing a connection that's not open is a noop.func (p*Pool) Close(c*Conn)
// SetMaxConnections sets the maximum number of connections that a pool can// maintain. If it sets to a number m that is smaller than the number of currently// open connections, SetMaxConnections should block (i.e., not return) until the// number of open connections drops below m.func (p*Pool) SetMaxConnections(mint)
// GetMaxConnections returns the maximum number of connections that the pool can// maintain.func (p*Pool) GetMaxConnections() int

You might have noticed that this assignment is similar to Go's database/sql package that you used in the last assignment. In particular, the DB type implements a connection pool internally.

To actually establish connections with a database, the sql package relies on database drivers. For example, github.com/mattn/go-sqlite3 package that we used in precepts and last assignment implements a driver for sqlite3. It is the driver that handles communication with the underlying database. For example, when calling db.Conn() in the sql package to get a new connection, it internally calls another function in the go-sqlite3 package which actually establishes a connection with the database.

For this assignment, we are removing the complexity of a driver component from your connection pool implementation. Instead, you can just use the newConnection function (passed in as input to NewPool()) to acquire a new function with a underlying database. You can think of newConnection as a driver's implementation of establishing a new connection. The important note to keep in mind is that you cannot assume this newConnection function to be safe for concurrent use.

You should not block by busy looping (e.g., while condition {}). Instead use something like condition variables.

Concurrency

All the APIs should be safe for concurrent use. There will be multiple threads (goroutines) that use the APIs concurrently. You need to make sure that your connection pool behaves correctly. For example, calling Open() from multiple goroutines should return unique values to each caller, assuming there are enough available connections in the pool.

Moreover, if SetMaxConnection() sets the pool size to a number smaller than the number of currently open connections, Open() should also block (i.e., do not open new connections to clients) until the number of open connections drops below the new maximum.

Lazy Allocation

Connection pool should allocate connections lazily (i.e. on demand). This means your implementation should not fill the entire pool with connections when it is first created. Instead it should only open connections with the underlying database when a client calls Open(). In fact, for this assignment, increment the total number of connections in your pool by only one when necessary.

Example Application

An example client is provided in 'client/main.go'. You can use that client to see how your connection pool will be used and test your implementation.

The example application also implements a simple database driver. You can see an example of a newConnection function passed into the NewPool() function. For actual grading, we are using a more complex driver but the interface is exactly the same.

Resources

Go sync package: Go's mutex, condition variable, etc.

Embedded mutexes: You might want to check out using mutexes as an embedded field.

Submission & Grading

Your assignment will be automatically submitted every time you push your changes to your GitHub repo. Within a couple minutes of your submission, the autograder will make a comment on your commit listing the output of our testing suite when run against your code. Note that you will be graded only on your changes to the conn_pool package, and not on your changes to any other files, though you may modify any files you wish.

You may submit and receive feedback in this way as many times as you like, whenever you like, but a substantial lateness penalty will be applied to submissions past the deadline.

About

Database Connection Pool

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

COS316, Assignment 5: Connection Pool

Due: April 13th at 11:00pm

Background

Establishing connections to a database often involves several expensive steps, such as creating a TCP channel with a database server, parsing a connection configuration string, and authenticating a user.

In practice, most applications (clients) use only one or a few different database connection configurations. This means that an application will repeatedly open and close many identical database connections.

Connection pooling is a way to avoid the overhead of reopening connections by reusing existing ones. A connection pool manages a set of established connections. When an application opens a connection to a database, the connection pool finds an existing connection and returns the connection to the application. When the application closes a connection, the connection pool adds the connection back to its list of available connections without actually closing the connection.

The diagram below illustrates a database connection pool at a high level:

DB Connection Pool

Objectives

The objective of this assignment is to implement a concurrent connection pool that manages a set of connections to a sql database. It should support the following APIs:

// Use this struct for storing pool related informationtypePoolstruct {
...
}
// A database connectiontypeConninterface {
Query(string) (*sql.Rows, error)
}
// NewPool creates a new connection pool with connections to a database.// It takes as input a function that actually establishes connections with// the underlying database.// Establishing a connection to a database might fail, in which case NewPool// propagates the error from the underlying driver to its caller.funcNewPool(newConnectionfunc() (Conn, error)) (*Pool, error)
// Open returns a connection from the connection pool. It should only return a// connection when the number of open (i.e., in-use) connections is less than the // maximum pool size. Open should block (i.e., not return) when the number of // open (i.e., in-use) connections exceeds the pool's maximum.// This function needs to be safe for concurrent use. When it is called by// multiple goroutines, it should return unique connections to each caller.func (p*Pool) Open() (*Conn)
// Close returns a connection back to the connection pool without actually closing// it with the underlying database.// When a connection is closed, using it has undefined behavior (i.e., applications// should not do it).// Close needs to return a connection to the pool it was originally allocated in.// If the connection does not belong to the pool (i.e., c was not allocated to p// originally), Close is a noop (i.e., no effects).// Closing a connection that's not open is a noop.func (p*Pool) Close(c*Conn)
// SetMaxConnections sets the maximum number of connections that a pool can// maintain. If it sets to a number m that is smaller than the number of currently// open connections, SetMaxConnections should block (i.e., not return) until the// number of open connections drops below m.func (p*Pool) SetMaxConnections(mint)
// GetMaxConnections returns the maximum number of connections that the pool can// maintain.func (p*Pool) GetMaxConnections() int

You might have noticed that this assignment is similar to Go's database/sql package that you used in the last assignment. In particular, the DB type implements a connection pool internally.

To actually establish connections with a database, the sql package relies on database drivers. For example, github.com/mattn/go-sqlite3 package that we used in precepts and last assignment implements a driver for sqlite3. It is the driver that handles communication with the underlying database. For example, when calling db.Conn() in the sql package to get a new connection, it internally calls another function in the go-sqlite3 package which actually establishes a connection with the database.

For this assignment, we are removing the complexity of a driver component from your connection pool implementation. Instead, you can just use the newConnection function (passed in as input to NewPool()) to acquire a new function with a underlying database. You can think of newConnection as a driver's implementation of establishing a new connection. The important note to keep in mind is that you cannot assume this newConnection function to be safe for concurrent use.

You should not block by busy looping (e.g., while condition {}). Instead use something like condition variables.

Concurrency

All the APIs should be safe for concurrent use. There will be multiple threads (goroutines) that use the APIs concurrently. You need to make sure that your connection pool behaves correctly. For example, calling Open() from multiple goroutines should return unique values to each caller, assuming there are enough available connections in the pool.

Moreover, if SetMaxConnection() sets the pool size to a number smaller than the number of currently open connections, Open() should also block (i.e., do not open new connections to clients) until the number of open connections drops below the new maximum.

Lazy Allocation

Connection pool should allocate connections lazily (i.e. on demand). This means your implementation should not fill the entire pool with connections when it is first created. Instead it should only open connections with the underlying database when a client calls Open(). In fact, for this assignment, increment the total number of connections in your pool by only one when necessary.

Example Application

An example client is provided in 'client/main.go'. You can use that client to see how your connection pool will be used and test your implementation.

The example application also implements a simple database driver. You can see an example of a newConnection function passed into the NewPool() function. For actual grading, we are using a more complex driver but the interface is exactly the same.

Resources

Go sync package: Go's mutex, condition variable, etc.

Embedded mutexes: You might want to check out using mutexes as an embedded field.

Submission & Grading

Your assignment will be automatically submitted every time you push your changes to your GitHub repo. Within a couple minutes of your submission, the autograder will make a comment on your commit listing the output of our testing suite when run against your code. Note that you will be graded only on your changes to the conn_pool package, and not on your changes to any other files, though you may modify any files you wish.

You may submit and receive feedback in this way as many times as you like, whenever you like, but a substantial lateness penalty will be applied to submissions past the deadline.

About

Database Connection Pool

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

COS316, Assignment 5: Connection Pool

Due: April 13th at 11:00pm

Background

Establishing connections to a database often involves several expensive steps, such as creating a TCP channel with a database server, parsing a connection configuration string, and authenticating a user.

In practice, most applications (clients) use only one or a few different database connection configurations. This means that an application will repeatedly open and close many identical database connections.

Connection pooling is a way to avoid the overhead of reopening connections by reusing existing ones. A connection pool manages a set of established connections. When an application opens a connection to a database, the connection pool finds an existing connection and returns the connection to the application. When the application closes a connection, the connection pool adds the connection back to its list of available connections without actually closing the connection.

The diagram below illustrates a database connection pool at a high level:

DB Connection Pool

Objectives

The objective of this assignment is to implement a concurrent connection pool that manages a set of connections to a sql database. It should support the following APIs:

// Use this struct for storing pool related informationtypePoolstruct {
...
}
// A database connectiontypeConninterface {
Query(string) (*sql.Rows, error)
}
// NewPool creates a new connection pool with connections to a database.// It takes as input a function that actually establishes connections with// the underlying database.// Establishing a connection to a database might fail, in which case NewPool// propagates the error from the underlying driver to its caller.funcNewPool(newConnectionfunc() (Conn, error)) (*Pool, error)
// Open returns a connection from the connection pool. It should only return a// connection when the number of open (i.e., in-use) connections is less than the // maximum pool size. Open should block (i.e., not return) when the number of // open (i.e., in-use) connections exceeds the pool's maximum.// This function needs to be safe for concurrent use. When it is called by// multiple goroutines, it should return unique connections to each caller.func (p*Pool) Open() (*Conn)
// Close returns a connection back to the connection pool without actually closing// it with the underlying database.// When a connection is closed, using it has undefined behavior (i.e., applications// should not do it).// Close needs to return a connection to the pool it was originally allocated in.// If the connection does not belong to the pool (i.e., c was not allocated to p// originally), Close is a noop (i.e., no effects).// Closing a connection that's not open is a noop.func (p*Pool) Close(c*Conn)
// SetMaxConnections sets the maximum number of connections that a pool can// maintain. If it sets to a number m that is smaller than the number of currently// open connections, SetMaxConnections should block (i.e., not return) until the// number of open connections drops below m.func (p*Pool) SetMaxConnections(mint)
// GetMaxConnections returns the maximum number of connections that the pool can// maintain.func (p*Pool) GetMaxConnections() int

You might have noticed that this assignment is similar to Go's database/sql package that you used in the last assignment. In particular, the DB type implements a connection pool internally.

To actually establish connections with a database, the sql package relies on database drivers. For example, github.com/mattn/go-sqlite3 package that we used in precepts and last assignment implements a driver for sqlite3. It is the driver that handles communication with the underlying database. For example, when calling db.Conn() in the sql package to get a new connection, it internally calls another function in the go-sqlite3 package which actually establishes a connection with the database.

For this assignment, we are removing the complexity of a driver component from your connection pool implementation. Instead, you can just use the newConnection function (passed in as input to NewPool()) to acquire a new function with a underlying database. You can think of newConnection as a driver's implementation of establishing a new connection. The important note to keep in mind is that you cannot assume this newConnection function to be safe for concurrent use.

You should not block by busy looping (e.g., while condition {}). Instead use something like condition variables.

Concurrency

All the APIs should be safe for concurrent use. There will be multiple threads (goroutines) that use the APIs concurrently. You need to make sure that your connection pool behaves correctly. For example, calling Open() from multiple goroutines should return unique values to each caller, assuming there are enough available connections in the pool.

Moreover, if SetMaxConnection() sets the pool size to a number smaller than the number of currently open connections, Open() should also block (i.e., do not open new connections to clients) until the number of open connections drops below the new maximum.

Lazy Allocation

Connection pool should allocate connections lazily (i.e. on demand). This means your implementation should not fill the entire pool with connections when it is first created. Instead it should only open connections with the underlying database when a client calls Open(). In fact, for this assignment, increment the total number of connections in your pool by only one when necessary.

Example Application

An example client is provided in 'client/main.go'. You can use that client to see how your connection pool will be used and test your implementation.

The example application also implements a simple database driver. You can see an example of a newConnection function passed into the NewPool() function. For actual grading, we are using a more complex driver but the interface is exactly the same.

Resources

Go sync package: Go's mutex, condition variable, etc.

Embedded mutexes: You might want to check out using mutexes as an embedded field.

Submission & Grading

Your assignment will be automatically submitted every time you push your changes to your GitHub repo. Within a couple minutes of your submission, the autograder will make a comment on your commit listing the output of our testing suite when run against your code. Note that you will be graded only on your changes to the conn_pool package, and not on your changes to any other files, though you may modify any files you wish.

You may submit and receive feedback in this way as many times as you like, whenever you like, but a substantial lateness penalty will be applied to submissions past the deadline.

About

Database Connection Pool

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

COS316, Assignment 5: Connection Pool

Due: April 13th at 11:00pm

Background

Establishing connections to a database often involves several expensive steps, such as creating a TCP channel with a database server, parsing a connection configuration string, and authenticating a user.

In practice, most applications (clients) use only one or a few different database connection configurations. This means that an application will repeatedly open and close many identical database connections.

Connection pooling is a way to avoid the overhead of reopening connections by reusing existing ones. A connection pool manages a set of established connections. When an application opens a connection to a database, the connection pool finds an existing connection and returns the connection to the application. When the application closes a connection, the connection pool adds the connection back to its list of available connections without actually closing the connection.

The diagram below illustrates a database connection pool at a high level:

DB Connection Pool

Objectives

The objective of this assignment is to implement a concurrent connection pool that manages a set of connections to a sql database. It should support the following APIs:

// Use this struct for storing pool related informationtypePoolstruct {
...
}
// A database connectiontypeConninterface {
Query(string) (*sql.Rows, error)
}
// NewPool creates a new connection pool with connections to a database.// It takes as input a function that actually establishes connections with// the underlying database.// Establishing a connection to a database might fail, in which case NewPool// propagates the error from the underlying driver to its caller.funcNewPool(newConnectionfunc() (Conn, error)) (*Pool, error)
// Open returns a connection from the connection pool. It should only return a// connection when the number of open (i.e., in-use) connections is less than the // maximum pool size. Open should block (i.e., not return) when the number of // open (i.e., in-use) connections exceeds the pool's maximum.// This function needs to be safe for concurrent use. When it is called by// multiple goroutines, it should return unique connections to each caller.func (p*Pool) Open() (*Conn)
// Close returns a connection back to the connection pool without actually closing// it with the underlying database.// When a connection is closed, using it has undefined behavior (i.e., applications// should not do it).// Close needs to return a connection to the pool it was originally allocated in.// If the connection does not belong to the pool (i.e., c was not allocated to p// originally), Close is a noop (i.e., no effects).// Closing a connection that's not open is a noop.func (p*Pool) Close(c*Conn)
// SetMaxConnections sets the maximum number of connections that a pool can// maintain. If it sets to a number m that is smaller than the number of currently// open connections, SetMaxConnections should block (i.e., not return) until the// number of open connections drops below m.func (p*Pool) SetMaxConnections(mint)
// GetMaxConnections returns the maximum number of connections that the pool can// maintain.func (p*Pool) GetMaxConnections() int

You might have noticed that this assignment is similar to Go's database/sql package that you used in the last assignment. In particular, the DB type implements a connection pool internally.

To actually establish connections with a database, the sql package relies on database drivers. For example, github.com/mattn/go-sqlite3 package that we used in precepts and last assignment implements a driver for sqlite3. It is the driver that handles communication with the underlying database. For example, when calling db.Conn() in the sql package to get a new connection, it internally calls another function in the go-sqlite3 package which actually establishes a connection with the database.

For this assignment, we are removing the complexity of a driver component from your connection pool implementation. Instead, you can just use the newConnection function (passed in as input to NewPool()) to acquire a new function with a underlying database. You can think of newConnection as a driver's implementation of establishing a new connection. The important note to keep in mind is that you cannot assume this newConnection function to be safe for concurrent use.

You should not block by busy looping (e.g., while condition {}). Instead use something like condition variables.

Concurrency

All the APIs should be safe for concurrent use. There will be multiple threads (goroutines) that use the APIs concurrently. You need to make sure that your connection pool behaves correctly. For example, calling Open() from multiple goroutines should return unique values to each caller, assuming there are enough available connections in the pool.

Moreover, if SetMaxConnection() sets the pool size to a number smaller than the number of currently open connections, Open() should also block (i.e., do not open new connections to clients) until the number of open connections drops below the new maximum.

Lazy Allocation

Connection pool should allocate connections lazily (i.e. on demand). This means your implementation should not fill the entire pool with connections when it is first created. Instead it should only open connections with the underlying database when a client calls Open(). In fact, for this assignment, increment the total number of connections in your pool by only one when necessary.

Example Application

An example client is provided in 'client/main.go'. You can use that client to see how your connection pool will be used and test your implementation.

The example application also implements a simple database driver. You can see an example of a newConnection function passed into the NewPool() function. For actual grading, we are using a more complex driver but the interface is exactly the same.

Resources

Go sync package: Go's mutex, condition variable, etc.

Embedded mutexes: You might want to check out using mutexes as an embedded field.

Submission & Grading

Your assignment will be automatically submitted every time you push your changes to your GitHub repo. Within a couple minutes of your submission, the autograder will make a comment on your commit listing the output of our testing suite when run against your code. Note that you will be graded only on your changes to the conn_pool package, and not on your changes to any other files, though you may modify any files you wish.

You may submit and receive feedback in this way as many times as you like, whenever you like, but a substantial lateness penalty will be applied to submissions past the deadline.

About

Database Connection Pool

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

COS316, Assignment 5: Connection Pool

Due: April 13th at 11:00pm

Background

Establishing connections to a database often involves several expensive steps, such as creating a TCP channel with a database server, parsing a connection configuration string, and authenticating a user.

In practice, most applications (clients) use only one or a few different database connection configurations. This means that an application will repeatedly open and close many identical database connections.

Connection pooling is a way to avoid the overhead of reopening connections by reusing existing ones. A connection pool manages a set of established connections. When an application opens a connection to a database, the connection pool finds an existing connection and returns the connection to the application. When the application closes a connection, the connection pool adds the connection back to its list of available connections without actually closing the connection.

The diagram below illustrates a database connection pool at a high level:

DB Connection Pool

Objectives

The objective of this assignment is to implement a concurrent connection pool that manages a set of connections to a sql database. It should support the following APIs:

// Use this struct for storing pool related informationtypePoolstruct {
...
}
// A database connectiontypeConninterface {
Query(string) (*sql.Rows, error)
}
// NewPool creates a new connection pool with connections to a database.// It takes as input a function that actually establishes connections with// the underlying database.// Establishing a connection to a database might fail, in which case NewPool// propagates the error from the underlying driver to its caller.funcNewPool(newConnectionfunc() (Conn, error)) (*Pool, error)
// Open returns a connection from the connection pool. It should only return a// connection when the number of open (i.e., in-use) connections is less than the // maximum pool size. Open should block (i.e., not return) when the number of // open (i.e., in-use) connections exceeds the pool's maximum.// This function needs to be safe for concurrent use. When it is called by// multiple goroutines, it should return unique connections to each caller.func (p*Pool) Open() (*Conn)
// Close returns a connection back to the connection pool without actually closing// it with the underlying database.// When a connection is closed, using it has undefined behavior (i.e., applications// should not do it).// Close needs to return a connection to the pool it was originally allocated in.// If the connection does not belong to the pool (i.e., c was not allocated to p// originally), Close is a noop (i.e., no effects).// Closing a connection that's not open is a noop.func (p*Pool) Close(c*Conn)
// SetMaxConnections sets the maximum number of connections that a pool can// maintain. If it sets to a number m that is smaller than the number of currently// open connections, SetMaxConnections should block (i.e., not return) until the// number of open connections drops below m.func (p*Pool) SetMaxConnections(mint)
// GetMaxConnections returns the maximum number of connections that the pool can// maintain.func (p*Pool) GetMaxConnections() int

You might have noticed that this assignment is similar to Go's database/sql package that you used in the last assignment. In particular, the DB type implements a connection pool internally.

To actually establish connections with a database, the sql package relies on database drivers. For example, github.com/mattn/go-sqlite3 package that we used in precepts and last assignment implements a driver for sqlite3. It is the driver that handles communication with the underlying database. For example, when calling db.Conn() in the sql package to get a new connection, it internally calls another function in the go-sqlite3 package which actually establishes a connection with the database.

For this assignment, we are removing the complexity of a driver component from your connection pool implementation. Instead, you can just use the newConnection function (passed in as input to NewPool()) to acquire a new function with a underlying database. You can think of newConnection as a driver's implementation of establishing a new connection. The important note to keep in mind is that you cannot assume this newConnection function to be safe for concurrent use.

You should not block by busy looping (e.g., while condition {}). Instead use something like condition variables.

Concurrency

All the APIs should be safe for concurrent use. There will be multiple threads (goroutines) that use the APIs concurrently. You need to make sure that your connection pool behaves correctly. For example, calling Open() from multiple goroutines should return unique values to each caller, assuming there are enough available connections in the pool.

Moreover, if SetMaxConnection() sets the pool size to a number smaller than the number of currently open connections, Open() should also block (i.e., do not open new connections to clients) until the number of open connections drops below the new maximum.

Lazy Allocation

Connection pool should allocate connections lazily (i.e. on demand). This means your implementation should not fill the entire pool with connections when it is first created. Instead it should only open connections with the underlying database when a client calls Open(). In fact, for this assignment, increment the total number of connections in your pool by only one when necessary.

Example Application

An example client is provided in 'client/main.go'. You can use that client to see how your connection pool will be used and test your implementation.

The example application also implements a simple database driver. You can see an example of a newConnection function passed into the NewPool() function. For actual grading, we are using a more complex driver but the interface is exactly the same.

Resources

Go sync package: Go's mutex, condition variable, etc.

Embedded mutexes: You might want to check out using mutexes as an embedded field.

Submission & Grading

Your assignment will be automatically submitted every time you push your changes to your GitHub repo. Within a couple minutes of your submission, the autograder will make a comment on your commit listing the output of our testing suite when run against your code. Note that you will be graded only on your changes to the conn_pool package, and not on your changes to any other files, though you may modify any files you wish.

You may submit and receive feedback in this way as many times as you like, whenever you like, but a substantial lateness penalty will be applied to submissions past the deadline.

About

Database Connection Pool

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages