Oracle NoSQL Database Python SDK - 5.4.1¶
This is the Python SDK for the Oracle NoSQL Database. Python 3.5+ is supported.
For information about the Oracle NoSQL Database see https://www.oracle.com/database/technologies/related/nosql.html
Installation¶
This topic describes how to install, configure, and use the Oracle NoSQL Database Python SDK. There are several supported environments:
- Oracle NoSQL Database Cloud Service
- Oracle NoSQL Database Cloud Simulator
- Oracle NoSQL Database on-premise
Prerequisites¶
The Python SDK requires:
- Python version 3.5 or later, running on Mac, Windows, or Linux.
- For the Oracle NoSQL Cloud Service:
- An Oracle Cloud Infrastructure account
- A user created in that account, in a group with a policy that grants the desired permissions.
- For the Oracle NoSQL Database Cloud Simulator:
- See Download the Oracle NoSQL Cloud Simulator to download and start the Cloud Simulator.
- For the on-premise Oracle NoSQL Database:
- An instance of the database (See Oracle NoSQL Database Downloads )
- A running proxy server, see Information about the proxy
Downloading and Installing the SDK¶
You can install the Python SDK through the Python Package Index (PyPI), or alternatively through GitHub.
GitHub¶
To install from GitHub:
Download the SDK from GitHub. The download is a zip containing a whl file and documentation.
Extract the files from the zip.
Use the following command to install the SDK:
pip install borneo-*-py2.py3-none-any.whl
Note
If you’re unable to install the whl file, make sure pip is up to date. Use
pip install -U pip
and then try to install the whl file again.
Configuring the SDK¶
This section describes configuring the SDK for the 3 environments supported. Skip to the section or sections of interest. The areas where the environments and use differ are
- Authentication and authorization. This is encapsulated in the AuthorizationProvider interface. The Cloud Service is secure and requires a Cloud Service identity as well as authorization for desired operations. The Cloud Simulator is not secure at all and requires no identity. The on-premise configuration can be either secure or not and it also requires an instance of the proxy service to access the database.
- API differences. Some classes and methods are specific to an environment. For example, the on-premise configuration includes methods to create namespaces and users and these concepts don’t exist in the cloud service. Similarly, the cloud service includes interfaces to specify and acquire throughput information on tables that is not relevant on-premise.
Configure for the Cloud Service¶
The SDK requires an Oracle Cloud account and a subscription to the Oracle NoSQL Database Cloud Service. If you do not already have an Oracle Cloud account you can start here. Credentials used for connecting an application are associated with a specific user. If needed, create a user for the person or system using the api. See Adding Users.
Using the SDK with the Oracle NoSQL Database Cloud Service also requires installation of the Oracle Cloud Infrastructure (OCI) Python SDK:
pip install oci
Acquire Credentials for the Oracle NoSQL Database Cloud Service¶
These steps only need to be performed one time for a user. If they have already been done they can be skipped. You need to obtain the following credentials:
- Tenancy ID
- User ID
- API signing key (private key in PEM format)
- Private key pass phrase, only needed if the private key is encrypted
- Fingerprint for the public key uploaded to the user’s account
See Required Keys and OCIDs for detailed descriptions of the above credentials and the steps you need to perform to obtain them. Specifically:
Supplying Credentials to an Application¶
Credentials are used to establish the initial connection from your application to the service. There are 2 ways to supply credentials to the application:
- Directly, via API
- Using a configuration file
Both mechanisms use borneo.iam.SignatureProvider
to handle credentials.
If using a configuration file it’s default location is $HOME/.oci/config, but
the location can be changed using the api.
The format of the configuration file is that of a properties file with the format of key=value, with one property per line. The contents and format are:
[DEFAULT]
tenancy=<your-tenancy-id>
user=<your-user-id>
fingerprint=<fingerprint-of-your-public-key>
key_file=<path-to-your-private-key-file>
pass_phrase=<optional-pass-phrase-for-key-file>
The Tenancy ID, User ID and fingerprint should be acquired using the
instructions above. The path to your private key file is the absolute path of
the RSA private key. The order of the properties does not matter. The
[DEFAULT] portion is the profile. A configuration file may contain multiple
profiles with the target profile specified in the
borneo.iam.SignatureProvider
parameters.
Provide credentials without a configuration file:
from borneo.iam import SignatureProvider
#
# Use SignatureProvider directly via API. Note that the
# private_key argument can either point to a key file or be the
# string content of the private key itself.
#
at_provider = SignatureProvider(
tenant_id='ocid1.tenancy.oc1..tenancy',
user_id='ocid1.user.oc1..user',
private_key=key_file_or_key,
fingerprint='fingerprint',
pass_phrase='mypassphrase')
Provide credentials using a configuration file in the default location, using the default profile:
from borneo.iam import SignatureProvider
#
# Use SignatureProvider with a default credentials file and
# profile $HOME/.oci/config
#
at_provider = SignatureProvider()
Provide credentials using a configuration file in a non-default location and non-default profile:
from borneo.iam import SignatureProvider
#
# Use SignatureProvider with a non-default credentials file and
# profile
#
at_provider = SignatureProvider(config_file='myconfigfile',
profile_name='myprofile')
Connecting an Application¶
The first step in any Oracle NoSQL Database Cloud Service application is to
create a handle used to send requests to the service. The handle is configured
using your credentials and other authentication information as well as the
endpoint to which the application will connect. An example endpoint is to use
the region Regions.US_ASHBURN_1. Information on regions can be found in
borneo.Regions
.
from borneo import NoSQLHandle, NoSQLHandleConfig, Regions
from borneo.iam import SignatureProvider
#
# Required information:
#
# the region to which the application will connect
region = Regions.US_ASHBURN_1
# if using a specified credentials file
credentials_file = <path-to-your-credentials-file>
#
# Create an AuthorizationProvider
#
at_provider = SignatureProvider(config_file=credentials_file)
#
# create a configuration object
#
config = NoSQLHandleConfig(region, at_provider)
#
# create a handle from the configuration object
#
handle = NoSQLHandle(config)
See examples and test code for specific details. Both of these use config*.py files for configuration of required information.
Configure for the Cloud Simulator¶
The Oracle NoSQL Cloud Simulator is a useful way to use this SDK to connect to a local server that supports the same protocol. The Cloud Simulator requires Java 8 or higher.
See Download the Oracle NoSQL Cloud Simulator to download and start the Cloud Simulator.
- Download and start the Cloud Simulator
- Follow instructions in the examples/config.py file for connecting examples to the Cloud Simulator. By default that file is configured to communicate with the Cloud Simulator, using default configuration.
The Cloud Simulator does not require the credentials and authentication information required by the Oracle NoSQL Database Cloud Service. The Cloud Simulator should not be used for deploying applications or important data.
Before using the Cloud Service it is recommended that users start with the Cloud Simulator to become familiar with the interfaces supported by the SDK.
Configure for the On-Premise Oracle NoSQL Database¶
The on-premise configuration requires a running instance of the Oracle NoSQL database. In addition a running proxy service is required. See Oracle NoSQL Database Downloads for downloads, and see Information about the proxy for proxy configuration information.
If running a secure store, a certificate path should be specified through the REQUESTS_CA_BUNDLE environment variable:
$ export REQUESTS_CA_BUNDLE=<path-to-certificate>/certificate.pem:$REQUESTS_CA_BUNDLE
Or borneo.NoSQLHandleConfig.set_ssl_ca_certs()
.
In addition, a user identity must be created in the store (separately) that has
permission to perform the required operations of the application, such as
manipulated tables and data. The identity is used in the
borneo.kv.StoreAccessTokenProvider
.
If the store is not secure, an empty instance of
borneo.kv.StoreAccessTokenProvider
is used. For example:
from borneo import NoSQLHandle, NoSQLHandleConfig
from borneo.kv import StoreAccessTokenProvider
#
# Assume the proxy is running on localhost:8080
#
endpoint = 'http://localhost:8080'
#
# Assume the proxy is secure and running on localhost:443
#
endpoint = 'https://localhost:443'
#
# Create the AuthorizationProvider for a secure store:
#
ap = StoreAccessTokenProvider(user_name, password)
#
# Create the AuthorizationProvider for a not secure store:
#
ap = StoreAccessTokenProvider()
#
# create a configuration object
#
config = NoSQLHandleConfig(endpoint).set_authorization_provider(ap)
#
# set the certificate path if running a secure store
#
config.set_ssl_ca_certs(<ca_certs>)
#
# create a handle from the configuration object
#
handle = NoSQLHandle(config)
Working With Tables¶
Applications using the Oracle NoSQL Database work with tables. Tables are created and data is added, modified and removed. Indexes can be added on tables. These topics are covered. Not all options and functions are described here. Detailed descriptions of interfaces can be found in API Reference.
Obtain a NoSQL Handle¶
borneo.NoSQLHandle
represents a connection to the service. Once created
it must be closed using the method borneo.NoSQLHandle.close()
in order to
clean up resources. Handles are thread-safe and intended to be shared. A handle
is created by first creating a borneo.NoSQLHandleConfig
instance to
configure the communication endpoint, authorization information, as well as
default values for handle configuration.
Configuration requires an borneo.AuthorizationProvider
to provide
identity and authorization information to the handle. There are different
instances of this class for the different environments:
- Oracle NoSQL Cloud Service
- Oracle NoSQL Cloud Simulator
- Oracle NoSQL Database on-premise
About Compartments¶
In the Oracle NoSQL Cloud Service environment tables are always created in an
Oracle Cloud Infrastructure compartment (see Managing Compartments). It is recommended that compartments be created for tables to better
organize them and control security, which is a feature of compartments. When
authorized as a specific user the default compartment for tables is the root
compartment of the user’s tenancy. A method exists to allow specification of a
default compartment for requests in
borneo.NoSQLHandleConfig.set_compartment()
. This overrides the user’s
default. In addition it is possible to specify a compartment is each
Request
instance.
The set_compartment methods take either an id (OCID) or a compartment name or path. If a compartment name is used it may be the name of a top-level compartment. If a compartment path is used to reference a nested compartment, the path is a dot-separate path that excludes the top-level compartment of the path, for example compartmentA.compartmentB.
Instead of setting a compartment in the request it is possible to use a compartment name to prefix a table name in a request, query, or DDL statement. This usage overrides any other setting of the compartment. E.g.
...
request = PutRequest().set_table_name('mycompartment:mytable')
...
create_statement = 'create table mycompartment:mytable(...)'
...
request = GetRequest().set_table_name('compartmentA.compartmentB')
If the application is authorized using an instance principal (see
borneo.iam.SignatureProvider.create_with_instance_principal()
) a
compartment must be specified either using a default or in each request, and it
must be specified as an id, as there is no default root compartment in this
path.
An example of acquiring a NoSQL Handle for the Oracle NoSQL Cloud Service:
from borneo import NoSQLHandle, NoSQLHandleConfig, Regions
from borneo.iam import SignatureProvider
# create AuthorizationProvider
provider = SignatureProvider()
# create handle config using the correct desired region as endpoint, add a
# default compartment.
config = NoSQLHandleConfig(Regions.US_ASHBURN_1).set_authorization_provider(
provider).set_default_compartment('mycompartment')
# create the handle
handle = NoSQLHandle(config)
An example using the on-premise Oracle NoSQL Database in a secure configuration, a certificate path should be specified through the REQUESTS_CA_BUNDLE environment variable:
$ export REQUESTS_CA_BUNDLE=<path-to-certificate>/certificate.pem:$REQUESTS_CA_BUNDLE
Or borneo.NoSQLHandleConfig.set_ssl_ca_certs()
, for example:
from borneo import NoSQLHandle, NoSQLHandleConfig
from borneo.kv import StoreAccessTokenProvider
# create AuthorizationProvider
provider = StoreAccessTokenProvider(<user_name>, <password>)
# create handle config using the correct endpoint for the running proxy
config = NoSQLHandleConfig(
'https://localhost:443').set_authorization_provider(
provider).set_ssl_ca_certs(<ca_certs>)
# create the handle
handle = NoSQLHandle(config)
To reduce resource usage and overhead of handle creation it is best to avoid
excessive creation and closing of borneo.NoSQLHandle
instances.
Create Tables and Indexes¶
Learn how to create tables and indexes in Oracle NoSQL Database.
Creating a table is the first step of developing your application. You use the
borneo.TableRequest
class and its methods to execute Data Definition
Language (DDL) statements, such as, creating, modifying, and dropping tables. If
using the Oracle NoSQL Cloud Service or Cloud Simulator you must also set table
limits using borneo.TableRequest.set_table_limits()
method. Limits are
ignored on-premise, if provided.
Before creating a table, learn about:
The supported data types for Oracle NoSQL Database. See Supported Data Types. Also see Data Types for a description of how database types map to Python.
For the Oracle NoSQL Database Cloud Service limits. See Oracle NoSQL Database Cloud Limits. These limits are not relevant on-premise.
Examples of DDL statements are:
# Create a new table called users
CREATE IF NOT EXISTS users (id INTEGER, name STRING, PRIMARY KEY (id))
# Create a new table called users and set the TTl value to 4 days
CREATE IF NOT EXISTS users (id INTEGER, name STRING, PRIMARY KEY (id))
USING TTL 4 days
# Create a new index called nameIdx on the name field in the users table
CREATE INDEX IF NOT EXISTS nameIdx ON users(name)
DDL statements are executing using the borneo.TableRequest
class. All
calls to borneo.NoSQLHandle.table_request()
are asynchronous so it is
necessary to check the result and call
borneo.TableResult.wait_for_completion()
to wait for operation to
complete. The convenience method, borneo.NoSQLHandle.do_table_request()
,
exists to combine execution of the operation with waiting for completion.
from borneo import TableLimits, TableRequest
statement = 'create table if not exists users(id integer, name string, ' +
'primary key(id)'
# In the Cloud Service TableLimits is a required object for table creation.
# It specifies the throughput and capacity for the table in ReadUnits,
# WriteUnits, GB
request = TableRequest().set_statement(statement).set_table_limits(
TableLimits(20, 10, 5))
# assume that a handle has been created, as handle, make the request wait
# for 40 seconds, polling every 3 seconds
result = handle.do_table_request(request, 40000, 3000)
# the above call to do_table_request is equivalent to
result = handle.table_request(request)
result.wait_for_completion(handle, 40000, 3000)
Add Data¶
Add rows to your table.
When you store data in table rows, your application can easily retrieve, add to, or delete information from the table.
The borneo.PutRequest
class represents input to the
borneo.NoSQLHandle.put()
method used to insert single rows. This method
can be used for unconditional and conditional puts to:
- Overwrite any existing row. This is the default.
- Succeed only if the row does not exist. Use
borneo.PutOption.IF_ABSENT
for this case.- Succeed only if the row exists. Use
borneo.PutOption.IF_PRESENT
for this case.- Succeed only if the row exists and its
borneo.Version
matches a specificborneo.Version
. Useborneo.PutOption.IF_VERSION
for this case andborneo.PutRequest.set_match_version()
to specify the version to match.
Options can be set using borneo.PutRequest.set_option()
.
To add rows to your table:
from borneo import PutRequest
# PutRequest requires a table name
request = PutRequest().set_table_name('users')
# set the value
request.set_value({'id': i, 'name': 'myname'})
result = handle.put(request)
# a successful put returns a non-empty version
if result.get_version() is not None:
# success
When adding data the values supplied must accurately correspond to the schema for the table. If they do not, IllegalArgumentException is raised. Columns with default or nullable values can be left out without error, but it is recommended that values be provided for all columns to avoid unexpected defaults. By default, unexpected columns are ignored silently, and the value is put using the expected columns.
If you have multiple rows that share the same shard key they can be put in a
single request using borneo.WriteMultipleRequest
which can be created
using a number of PutRequest or DeleteRequest objects.
You can also add JSON data to your table. In the case of a fixed-schema table the JSON is converted to the target schema. JSON data can be directly inserted into a column of type JSON. The use of the JSON data type allows you to create table data without a fixed schema, allowing more flexible use of the data.
Add JSON Data¶
The data value provided for a row or key is a Python dict. It can be supplied to the relevant requests (GetRequest, PutRequest, DeleteRequest) in multiple ways:
as a Python dict directly:
request.set_value({'id': 1}) request.set_key({'id': 1 })as a JSON string:
request.set_value_from_json('{"id": 1, "name": "myname"}') request.set_key_from_json('{"id": 1}')
In both cases the keys and values provided must accurately correspond to the
schema of the table. If not an borneo.IllegalArgumentException
exception is raised. If the data is provided as JSON and the JSON cannot be
parsed a ValueError
is raised.
Read Data¶
Learn how to read data from your table.
You can read single rows using the borneo.NoSQLHandle.get()
method. This
method allows you to retrieve a record based on its primary key value. In order
to read multiple rows in a single request see Use Queries, below.
The borneo.GetRequest
class is used for simple get operations. It
contains the primary key value for the target row and returns an instance of
borneo.GetResult
.
from borneo import GetRequest
# GetRequest requires a table name
request = GetRequest().set_table_name('users')
# set the primary key to use
request.set_key({'id': 1})
result = handle.get(request)
# on success the value is not empty
if result.get_value() is not None:
# success
By default all read operations are eventually consistent, using
borneo.Consistency.EVENTUAL
. This type of read is less costly than
those using absolute consistency, borneo.Consistency.ABSOLUTE
. This
default can be changed in borneo.NoSQLHandle
using
borneo.NoSQLHandleConfig.set_consistency()
before creating the handle. It
can be changed for a single request using
borneo.GetRequest.set_consistency()
.
Use Queries¶
Learn about using queries in your application.
Oracle NoSQL Database provides a rich query language to read and update data. See the SQL For NoSQL Specification for a full description of the query language.
There are two ways to get the results of a query: using an iterator or loop through partial results.
Iterator¶
Use borneo.NoSQLHandle.query_iterable()
to get an iterable
that contains all the results of a query. Usage example:
from borneo import QueryRequest
handle = ...
statement = 'select * from users where name = "Taylor"'
request = QueryRequest().set_statement(statement)
qiresult = handle.query_iterable(request)
for row in qiresult:
# do something with the result row
print(row)
Partial results¶
Another way is to loop through partial results by using the
borneo.NoSQLHandle.query()
method. For example, to execute a SELECT
query to read data from your table, a borneo.QueryResult
contains a
list of results. And if the borneo.QueryRequest.is_done()
returns False,
there may be more results, so queries should generally be run in a loop. It is
possible for single request to return no results but the query still not done,
indicating that the query loop should continue. For example:
from borneo import QueryRequest
# Query at table named 'users" using the field 'name' where name may match 0
# or more rows in the table. The table name is inferred from the query
# statement
statement = 'select * from users where name = "Taylor"'
request = QueryRequest().set_statement(statement)
# loop until request is done, handling results as they arrive
while True:
result = handle.query(request)
# handle results
handle_results(result) # do something with results
if request.is_done():
break
When using queries it is important to be aware of the following considerations:
- Oracle NoSQL Database provides the ability to prepare queries for execution and reuse. It is recommended that you use prepared queries when you run the same query for multiple times. When you use prepared queries, the execution is much more efficient than starting with a query string every time. The query language and API support query variables to assist with query reuse. See
borneo.NoSQLHandle.prepare()
andborneo.PrepareRequest
for more information.- The
borneo.QueryRequest
allows you to set the read consistency for a query as well as modifying the maximum amount of resource (read and write) to be used by a single request. This can be important to prevent a query from getting throttled because it uses too much resource too quickly.
Here is an example of using a prepared query with a single variable:
from borneo import PrepareRequest, QueryRequest
# Use a similar query to above but make the name a variable
statement = 'declare $name string; select * from users where name = $name'
prequest = PrepareRequest().set_statement(statement)
presult = handle.prepare(prequest)
# use the prepared statement, set the variable
pstatement = presult.get_prepared_statement()
pstatement.set_variable('$name', 'Taylor')
qrequest = QueryRequest().set_prepared_statement(pstatement)
qiresult = handle.query_iterable(qrequest)
# loop on all the results
for row in qiresult:
# do something with the result row
print(row)
# use a different variable value with the same prepared query
pstatement.set_variable('$name', 'another_name')
qrequest = QueryRequest().set_prepared_statement(pstatement)
# loop until qrequest is done, handling results as they arrive
while True:
# use the prepared query in the query request
qresult = handle.query(qrequest)
# handle results
handle_results(qresult) # do something with results
if qrequest.is_done():
break
Delete Data¶
Learn how to delete rows from your table.
Single rows are deleted using borneo.DeleteRequest
using a primary key
value:
from borneo import DeleteRequest
# DeleteRequest requires table name and primary key
request = DeleteRequest().set_table_name('users')
request.set_key({'id': 1})
# perform the operation
result = handle.delete(request)
if result.get_success():
# success -- the row was deleted
# if the row didn't exist or was not deleted for any other reason, False is
# returned
Delete operations can be conditional based on a borneo.Version
returned
from a get operation. See borneo.DeleteRequest
.
You can perform multiple deletes in a single operation using a value range using
borneo.MultiDeleteRequest
and borneo.NoSQLHandle.multi_delete()
.
Modify Tables¶
Learn how to modify tables. You modify a table to:
- Add or remove fields to an existing table
- Change the default TimeToLive (TTL) value for the table
- Modify table limits
Examples of DDL statements to modify a table are:
# Add a new field to the table
ALTER TABLE users (ADD age INTEGER)
# Drop an existing field from the table
ALTER TABLE users (DROP age)
# Modify the default TTl value
ALTER TABLE users USING TTL 4 days
If using the Oracle NoSQL Database Cloud Service table limits can be modified
using borneo.TableRequest.set_table_limits()
, for example:
from borneo import TableLimits, TableRequest
# in this path the table name is required, as there is no DDL statement
request = TableRequest().set_table_name('users')
request.set_table_limits(TableLimits(40, 10, 5))
result = handle.table_request(request)
# table_request is asynchronous, so wait for the operation to complete, wait
# for 40 seconds, polling every 3 seconds
result.wait_for_completion(handle, 40000, 3000)
Delete Tables and Indexes¶
Learn how to delete a table or index.
To drop a table or index, use the drop table or drop index DDL statement, for example:
# drop the table named users (implicitly drops any indexes on that table)
DROP TABLE users
# drop the index called nameIndex on the table users. Don't fail if the index
# doesn't exist
DROP INDEX IF EXISTS nameIndex ON users
from borneo import TableRequest
# the drop statement
statement = 'drop table users'
request = TableRequest().set_statement(statement)
# perform the operation, wait for 40 seconds, polling every 3 seconds
result = handle.do_table_request(request, 40000, 3000)
Handle Errors¶
Python errors are raised as exceptions defined as part of the API. They are all
instances of Python’s RuntimeError
. Most exceptions are instances of
borneo.NoSQLException
which is a base class for exceptions raised by
the Python driver.
- Exceptions are split into 2 broad categories:
- Exceptions that may be retried with the expectation that they may succeed on
retry. These are all instances of
borneo.RetryableException
. Examples of these are the instances ofborneo.ThrottlingException
which is raised when resource consumption limits are exceeded. - Exceptions that should not be retried, as they will fail again. Examples of
these include
borneo.IllegalArgumentException
,borneo.TableNotFoundException
, etc.
- Exceptions that may be retried with the expectation that they may succeed on
retry. These are all instances of
borneo.ThrottlingException
instances will never be thrown in an
on-premise configuration as there are no relevant limits.
Handle Resource Limits¶
This section is relevant only to the Cloud Service and Simulator.
Programming in a resource-limited environment can be unfamiliar and can lead to
unexpected errors. Tables have user-specified throughput limits and if an
application exceeds those limits it may be throttled, which means requests will
raise instances of borneo.ThrottlingException
.
There is some support for built-in retries and users can create their own
borneo.RetryHandler
instances to be set using
borneo.NoSQLHandleConfig.set_retry_handler()
allowing more direct control
over retries as well as tracing of throttling events. An application should not
rely on retries to handle throttling exceptions as that will result in poor
performance and an inability to use all of the throughput available for the
table. This happens because the default retry handler will do exponential
backoff, starting with a one-second delay.
While handling borneo.ThrottlingException
is necessary it is best to
avoid throttling entirely by rate-limiting your application. In this context
rate-limiting means keeping request rates under the limits for the table. This
is most common using queries, which can read a lot of data, using up capacity
very quickly. It can also happen for get and put operations that run in a tight
loop. Some tools to control your request rate include:
- use the methods available in all Result objects that indicate how much read and write throughput was used by that request. For example, see
borneo.GetResult.get_read_units()
orborneo.PutResult.get_write_units()
.- reduce the default amount of data read for a single query request by using
borneo.QueryRequest.set_max_read_kb()
. Remember to perform query operations in a loop, looking at the continuation key. Be aware that a single query request can return 0 results but still have a continuation key that means you need to keep looping.- add rate-limiting code in your request loop. This may be as simple as a delay between requests or intelligent code that considers how much data has been read (see
borneo.QueryResult.get_read_units()
) as well as the capacity of the table to either delay a request or reduce the amount of data to be read.
Data Types¶
This topic describes the mapping between types in the Oracle NoSQL Database and Python data types. The database types are referred to as database types while the Python equivalents are Python types.
Oracle NoSQL Database Types¶
See Supported Data Types for a description of the data types supported by the service. An application uses these types to create tables and indexes. For example, a table may be created using this Data Definition Language (DDL) statement, which defines types in terms of the database types:
create table mytable(id integer, name string, created timestamp,
address record(street string, city string, zip integer), primary key(id))
In order to insert rows into such a table your application must create a Python dict that corresponds to that schema, for example:
{'id': 1, 'name': 'myname', 'created': datetime.now(),
'address' : {'street' : '14 Elm Street', 'city' : 'hometown',
'zip' : 00000}}
Similarly, when operating on rows retrieved from the database it is important to understand the mappings to Python types.
Mapping Between Database and Python types¶
These mappings apply on both input (get/query) and output (put). In general the system is permissive in terms of valid conversions among types and that any lossless conversion is allowed. For example an integer will be accepted for a float or double database type. The Timestamp type is also flexible and will accept any valid IS0 8601 formatted string. Timestamps are always stored and managed in UTC.
Database Type | Python Type |
---|---|
Integer | int |
Long | int (Python 3), long (Python2) |
Float | float |
Double | float |
Number | decimal.Decimal |
Boolean | bool |
String | str |
Timestamp | datetime.datetime |
Enum | str |
Binary | bytearray |
FixedBinary | bytearray |
Array | list |
Map | dict |
Record | dict |
JSON | any Python datatype that can be represented as JSON without data loss |
Timestamp in Borneo¶
As mentioned above Timestamp fields are managed internally as UTC time. If a timezone is supplied when setting a Timestamp, either as a string or as a Python datetime object, it will be honored. The value will be converted to UTC internally and will be in UTC when returned in a row. Although they are represented in UTC returned datetime objects will be “naive” as described by Python documentation. On input, if no timezone is supplied, python datetime instances and time strings are treated as UTC.
API Reference¶
borneo Package¶
Classes¶
AuthorizationProvider |
AuthorizationProvider is a callback interface used by the driver to obtain an authorization string for a request. |
BatchOperationNumberLimitException (message) |
Cloud service only. |
Consistency |
Set the consistency for read requests. |
Durability (master_sync, replica_sync, …) |
Durability defines the durability characteristics associated with a standalone write (put or update) operation. |
DefaultRetryHandler ([retries, delay_s]) |
Default retry handler. |
DeleteRequest () |
Represents the input to a NoSQLHandle.delete() operation. |
DeleteResult () |
Represents the result of a NoSQLHandle.delete() operation. |
FieldRange (field_path) |
FieldRange defines a range of values to be used in a NoSQLHandle.multi_delete() operation, as specified in MultiDeleteRequest.set_range() . |
GetIndexesRequest () |
Represents the argument of a NoSQLHandle.get_indexes() operation which returns the information of a specific index or all indexes of the specified table, as returned in GetIndexesResult . |
GetIndexesResult () |
Represents the result of a NoSQLHandle.get_indexes() operation. |
GetRequest () |
Represents the input to a NoSQLHandle.get() operation which returns a single row based on the specified key. |
GetResult () |
Represents the result of a NoSQLHandle.get() operation. |
GetTableRequest () |
Represents the argument of a NoSQLHandle.get_table() operation which returns static information associated with a table, as returned in TableResult . |
IllegalArgumentException ([message, cause]) |
Exception class that is used when an invalid argument was passed, this could mean that the type is not the expected or the value is not valid for the specific case. |
IllegalStateException ([message, cause]) |
Exception that is thrown when a method has been invoked at an illegal or inappropriate time. |
IndexExistsException (message) |
The operation attempted to create an index for a table but the named index already exists. |
IndexInfo (index_name, field_names[, field_types]) |
IndexInfo represents the information about a single index including its name, field names and field types. |
IndexNotFoundException (message) |
The operation attempted to access a index that does not exist or is not in a visible state. |
InvalidAuthorizationException (message) |
The exception is thrown if the application presents an invalid authorization string in a request. |
ListTablesRequest () |
Represents the argument of a NoSQLHandle.list_tables() operation which lists all available tables associated with the identity associated with the handle used for the operation. |
ListTablesResult () |
Represents the result of a NoSQLHandle.list_tables() operation. |
MultiDeleteRequest () |
Represents the input to a NoSQLHandle.multi_delete() operation which can be used to delete a range of values that match the primary key and range provided. |
MultiDeleteResult () |
Represents the result of a NoSQLHandle.multi_delete() operation. |
NoSQLException (message[, cause]) |
A base class for most exceptions thrown by the NoSQL driver. |
NoSQLHandle (config) |
NoSQLHandle is a handle that can be used to access Oracle NoSQL tables. |
NoSQLHandleConfig ([endpoint, provider]) |
An instance of this class is required by NoSQLHandle . |
OperationNotSupportedException (message) |
The operation attempted is not supported. |
OperationResult () |
A single Result associated with the execution of an individual operation in a NoSQLHandle.write_multiple() request. |
OperationThrottlingException (message) |
Cloud service only. |
PreparedStatement (sql_text, query_plan, …) |
A class encapsulating a prepared query statement. |
PrepareRequest () |
A request that encapsulates a query prepare call. |
PrepareResult () |
The result of a prepare operation. |
PutOption |
Set the put option for put requests. |
PutRequest () |
Represents the input to a NoSQLHandle.put() operation. |
PutResult () |
Represents the result of a NoSQLHandle.put() operation. |
QueryRequest () |
A request that represents a query. |
QueryResult (request[, computed]) |
QueryResult comprises a list of dict instances representing the query results. |
QueryIterableResult (request, handle) |
QueryIterableResult comprises an iterable list of dict instances representing all the query results. |
ReadThrottlingException (message) |
Cloud service only. |
Region (region_id) |
Cloud service only. |
Regions |
Cloud service only. |
Request () |
A request is a class used as a base for all requests types. |
RequestSizeLimitException (message) |
Cloud service only. |
RequestTimeoutException (message[, …]) |
Thrown when a request cannot be processed because the configured timeout interval is exceeded. |
ResourceExistsException (message) |
The operation attempted to create a resource but it already exists. |
ResourcePrincipalClaimKeys |
Claim keys in the resource principal session token(RPST). |
ResourceNotFoundException (message) |
The operation attempted to access a resource that does not exist or is not in a visible state. |
Result () |
Result is a base class for result classes for all supported operations. |
RetryHandler |
RetryHandler is called by the request handling system when a RetryableException is thrown. |
RetryableException (message) |
A base class for all exceptions that may be retried with a reasonable expectation that they may succeed on retry. |
SecurityInfoNotReadyException (message) |
Cloud service only. |
State |
Represents the table state. |
StatsControl (config, logger, …) |
StatsControl allows user to control the collection of driver statistics at |
StatsProfile |
The following semantics are attached to the StatsProfile values: |
SystemException (message) |
An exception that is thrown when there is an internal system problem. |
SystemRequest () |
On-premise only. |
SystemResult () |
On-premise only. |
SystemState |
On-premise only. |
SystemStatusRequest () |
On-premise only. |
TableExistsException (message) |
The operation attempted to create a table but the named table already exists. |
TableLimits (read_units, write_units, storage_gb) |
Cloud service only. |
TableNotFoundException (message) |
The operation attempted to access a table that does not exist or is not in a visible state. |
TableRequest () |
TableRequest is used to create, modify, and drop tables. |
TableResult () |
TableResult is returned from NoSQLHandle.get_table() and NoSQLHandle.table_request() operations. |
TableUsageRequest () |
Cloud service only. |
TableUsageResult () |
Cloud service only. |
ThrottlingException (message) |
Cloud service only. |
TimeToLive (value, timeunit) |
TimeToLive is a utility class that represents a period of time, similar to java.time.Duration in Java, but specialized to the needs of this driver. |
TimeUnit |
The time unit to use. |
UserInfo (user_id, user_name) |
On-premise only. |
Version (version) |
Version is an opaque class that represents the version of a row in the database. |
WriteMultipleRequest () |
Represents the input to a NoSQLHandle.write_multiple() operation. |
WriteMultipleResult () |
Represents the result of a NoSQLHandle.write_multiple() operation. |
WriteThrottlingException (message) |
Cloud service only. |
borneo.iam Package¶
Classes¶
SignatureProvider ([provider, config_file, …]) |
Cloud service only. |
borneo.kv Package¶
Classes¶
AuthenticationException (message[, cause]) |
On-premise only. |
StoreAccessTokenProvider ([user_name, password]) |
On-premise only. |
How to find client statistics¶
StatsControl allows user to control the collection of driver statistics at runtime.
The statistics data is collected for an interval of time. At the end of the interval, the stats data is logged in a specified JSON format that can be filtered and parsed. After the logging, the counters are cleared and collection of data resumes.
Collection intervals are aligned to the top of the hour. This means first interval logs may contain stats for a shorter interval.
How to enable and configure from command line¶
Collection of stats are controlled by the following environment variables:
NOSQL_STATS_PROFILE=[none|regular|more|all]
- Specifies the stats profile:
none
- disabled.regular
- per request: counters, errors, latencies, delays, retries.- This incurs minimum overhead.
more
- stats above with 95th and 99th percentile latencies.- This may add 0.5% overhead compared to none stats profile.
all
- stats above with per query information.- This may add 1% overhead compared to none stats profile.
NOSQL_STATS_INTERVAL=600
- Interval in seconds to log the stats, by default is 10 minutes.
NOSQL_STATS_PRETTY_PRINT=true
- Option to enable pretty printing of the JSON data, default value is false.
How to enable and configure using the API¶
Collection of stats can also be used by using the API:
NoSQLHandleConfig.set_stats_profile()
or
StatsControl.set_profile()
. At runtime stats collection can be
enabled selectively by using StatsControl.start()
ond
StatsControl.stop()
. The following example shows how to use a stats
handler and how to control the stats at runtime:
def stats_handler(stats):
# type: (Dict) -> None
print("Stats : " + str(stats))
...
config = NoSQLHandleConfig( endpoint )
config.set_stats_profile(StatsProfile.REGULAR)
config.set_stats_interval(600)
config.set_stats_pretty_print(False)
config.set_stats_handler(stats_handler)
handle = NoSQLHandle(config)
handle = get_handle(tenant_id)
stats_control = handle.get_stats_control()
#... application code without stats
# enable observations
stats_control.start();
#... application code with REGULAR stats
# For particular parts of code profile can be changed to collect more stats.
stats_control.set_stats_profile(StatsProfile.ALL)
#... more sensitive code with ALL stats
stats_control.set_stats_profile(StatsProfile.REGULAR)
#... application code with REGULAR stats
# disable observations
stats_control.stop()
#... application code without stats
handle.close()
Example log entry¶
The following is an example of stats log entry using the ALL profile:
- A one time entry containing stats id and options:
INFO: Client stats|{ // INFO log entry
"sdkName" : "Oracle NoSQL SDK for Python", // SDK name
"sdkVersion" : "5.2.4", // SDK version
"clientId" : "f595b333", // NoSQLHandle id
"profile" : "ALL", // stats profile
"intervalSec" : 600, // interval length in seconds
"prettyPrint" : true, // JSON pretty print
"rateLimitingEnabled" : false} // if rate limiting is enabled
- An entry at the end of each interval containing the stats values:
INFO: Client stats|{
"clientId" : "b7bc7734", // id of NoSQLHandle object
"startTime" : "2021-09-20T20:11:42Z", // UTC start interval time
"endTime" : "2021-09-20T20:11:47Z", // UTC end interval time
"requests" : [{ // array of types of requests
"name" : "Get", // stats for GET request type
"httpRequestCount" : 2, // count of http requests
"errors" : 0, // number of errors in interval
"httpRequestLatencyMs" : { // response time of http requests
"min" : 4, // minimum value in interval
"avg" : 4.5, // average value in interval
"max" : 5, // maximum value in interval
"95th" : 5, // 95th percentile value
"99th" : 5 // 99th percentile value
},
"requestSize" : { // http request size in bytes
"min" : 42, // minimum value in interval
"avg" : 42.5, // average value in interval
"max" : 43 // maximum value in interval
},
"resultSize" : { // http result size in bytes
"min" : 193, // minimum value in interval
"avg" : 206.5, // average value in interval
"max" : 220 // maximum value in interval
},
"rateLimitDelayMs" : 0, // delay in milliseconds introduced by the rate limiter
"retry" : { // retries
"delayMs" : 0, // delay in milliseconds introduced by retries
"authCount" : 0, // no of auth retries
"throttleCount" : 0, // no of throttle retries
"count" : 0 // total number of retries
}
}, {
"name" : "Query", // stats for all QUERY type requests
"httpRequestCount" : 14,
"errors" : 0,
"httpRequestLatencyMs" : {
"min" : 3,
"avg" : 13.0,
"max" : 32,
"95th" : 32,
"99th" : 32
},
"resultSize" : {
"min" : 146,
"avg" : 7379.71,
"max" : 10989
},
"requestSize" : {
"min" : 65,
"avg" : 709.85,
"max" : 799
},
"rateLimitDelayMs" : 0,
"retry" : {
"delayMs" : 0,
"authCount" : 0,
"throttleCount" : 0,
"count" : 0
}
}, {
"name" : "Put", // stats for PUT type requests
"httpRequestCount" : 1002,
"errors" : 0,
"httpRequestLatencyMs" : {
"min" : 1,
"avg" : 4.41,
"max" : 80,
"95th" : 8,
"99th" : 20
},
"requestSize" : {
"min" : 90,
"avg" : 90.16,
"max" : 187
},
"resultSize" : {
"min" : 58,
"avg" : 58.0,
"max" : 58
},
"rateLimitDelayMs" : 0,
"retry" : {
"delayMs" : 0,
"authCount" : 0,
"throttleCount" : 0,
"count" : 0
}
}],
"queries" : [{ // query stats aggregated by query statement
// query statement
"query" : "SELECT * FROM audienceData ORDER BY cookie_id",
// query plan description
"plan" : "SFW([6])
[
FROM:
RECV([3])
[
DistributionKind : ALL_PARTITIONS,
Sort Fields : sort_gen,
] as $from-0
SELECT:
FIELD_STEP([6])
[
VAR_REF($from-0)([3]),
audienceData
]
]",
"doesWrites" : false,
"httpRequestCount" : 12, // number of http calls to the server
"unprepared" : 1, // number of query requests without prepare
"simple" : false, // type of query
"count" : 20, // number of handle.query() API calls
"errors" : 0, // number of calls trowing exception
"httpRequestLatencyMs" : {// response time of http requests in milliseconds
"min" : 8, // minimum value in interval
"avg" : 14.58, // average value in interval
"max" : 32, // maximum value in interval
"95th" : 32, // 95th percentile value in interval
"99th" : 32 // 99th percentile value in interval
},
"requestSize" : { // http request size in bytes
"min" : 65, // minimum value in interval
"avg" : 732.5, // average value in interval
"max" : 799 // maximum value in interval
},
"resultSize" : { // http result size in bytes
"min" : 914, // minimum value in interval
"avg" : 8585.33, // average value in interval
"max" : 10989 // maximum value in interval
},
"rateLimitDelayMs" : 0, // total delay introduced by rate limiter in milliseconds
"retry" : { // automatic retries
"delayMs" : 0, // delay introduced by retries
"authCount" : 0, // count of auth related retries
"throttleCount" : 0, // count of throttle related retries
"count" : 0 // total count of retries
}
}]
The log entries go to the logger configured in NoSQLHandlerConfig. By default, if no logger is configured the statistics entries, if enabled, will be logged to file logs/driver.log in the local directory.
Stats collection is not dependent of logging configuration, even if logging is disabled, collection of stats will still happen if stats profile other than none is used. In this case, the stats are available by using the stats handler.
Depending on the type of query, if client processing is required, for
example in the case of ordered or aggregate queries, indicated by the
false simple field of the query entry, the count and
httpRequestsCount numbers will differ. count represents
the number of handle.query()
API calls and httpRequestCount
represents the number of internal http requests from server. For these
type of queries, the driver executes several simpler queries, per
shard or partition, and than combines the results locally.
Note: connection statistics are not available for NoSQL Python driver.