// File: best-practices/datacoves/README # Best practices on datacoves Some recommendations on using datacoves. --- // File: best-practices/datacoves/folder-structure # Organizing your project We recommend organizing your Datacoves project repository as described below so that different components are simple to find and maintain. View our sample analytics project for an example of all the required and recommended folders. ## Required Folders The following folders are required for Datacoves Setup. Be sure to add them to your repository. ### automate/ The `automate/` folder contains scripts that are used by automated jobs. ### automate/dbt/ The `automate/dbt/` folder has dbt specific scripts and the `profiles.yml` file used by automated jobs e.g. for Github Actions or Airflow. ### orchestrate/ The `orchestrate/` folder contains Airflow related files. ### orchestrate/dags The `orchestrate/dags` folder will contain the python dags that airflow will read ## Recommended Folders The following folders are optional. Some are recommended and others are only necessary for specific use cases. >[!NOTE] Below `DATACOVES__DBT_HOME` refers to the location of your dbt project (where you dbt_project.yml file is located). See [Datacoves Environment Variables](/docs/reference/vscode/datacoves-env-vars) for more information. ### DATACOVES__DBT_HOME/.dbt-coves This folder is only needed if you are using the [dbt-coves library](https://github.com/datacoves/dbt-coves?tab=readme-ov-file#dbt-coves). This show be at the same level as your dbt project. ie) The root or in the `transform` folder. ### DATACOVES__DBT_HOME/.dbt-coves/config.yml This folder is only needed if you are using the [dbt-coves library](https://github.com/datacoves/dbt-coves?tab=readme-ov-file#dbt-coves). dbt-coves will read the settings in this file to complete commands. Visit the [dbt-coves docs](https://github.com/datacoves/dbt-coves?tab=readme-ov-file#settings) for the full dbt-coves settings. ### DATACOVES__DBT_HOME/.dbt-coves/templates/ This folder is only needed if you are using the [dbt-coves library](https://github.com/datacoves/dbt-coves?tab=readme-ov-file#dbt-coves) and you want to override the dbt-coves sql and yml generators ### .github/workflows If you're working on a Github repository and using Github Actions for CI/CD, the `.github` folder holds the Github Action Workflows ### load/ The `load/` folder can contain extract and load configurations as well as other scripts or frameworks you may be using to extract and load data. ### secure/ The `secure/` folder contains warehouse security role definitions. The folder is only needed if you are using Snowflake and Permifrost. ### transform/ While you can keep your dbt project in your project's root folder, we recommend moving it into a `transform/` sub-folder. ### orchestrate/dag_yml_definitions The `orchestrate/dag_yml_definitions` is an optional folder that will contain yml dag definition files that dbt-coves will compile. This folder is only needed if you are using the dbt-coves extension to compile yml dags to python. ### orchestrate/python_scripts The `python_scripts` folder will contain custom python scripts that you can call from an Airflow DAG. This is needed only if using custom Python scripts in your Airflow DAGs. ### visualization/ The `visualization` folder is used to place configs related to superset or other visualization tools. ### visualization/streamlit The `visualization/streamlit` folder is used for Streamlit apps. This folder is only needed if using Streamlit. ### .vscode/settings.json The `.vscode/settings.json` folder is used for customized settings in order to override the default workspace settings. This file can contain secrets so be sure to add it to the `.gitignore` to avoid version control. See our [How to Override default VS sCode settings](/docs/how-tos/vs-code/override) for more info --- // File: best-practices/dbt/dbt-guidelines # dbt Guidelines ## dbt Recommendations - When a model has too much code, rather than using a subquery or CTE (with statement) to separate logic in a model, create a new model. This can be imported as an ephemeral model, but makes logic easier to manage. - All `ref` and `source` references in a model should be included with selected fields at the top of the file as a CTE (with statement); allowing quick visibility of where the data comes from in a given model. - Rather than adding new models to a team folder, think about where the model best fits in the overall project. Hierarchy and organizational projects can change, but your company will always care about 'Events' and 'Customers'. - Rather than creating a new model, first search for any existing models that may achieve the same outcome. While in the early stages most logic will be new, over time this will save a lot of development effort. - Rather than applying the same transformation manually in a number of places, consider creating a macro. It's a straightforward process for an experienced SQL developer and will make it much easier the next time you need it. - Rather than applying just the required tests & documentation to your code, consider what you can put in place to avoid ever seeing this model again. If the logic is well tested and documented (both in the description and with --inline comments for complex sections) it will stand the test of time. - Rather than describing the reason for a change in the column description, the reason for change should be listed in the git commit message; the description should only describe what is, not what used to be. - Rather than developing complex queries against all data in the Development environment, consider adding a where condition to run just the most recent data - it will speed up query time, and with a `{% if target.name == 'dev' %}` the where statement can be easily removed during testing and in production. - Rather than using *singular* tests, *generic* tests (defined in yml files) should be used wherever possible. - Where a *singular* test (defined in /testing) is required, the folder structure under /tests should match the structure in /models for the primary model being tested. - All models should be initially created as views or ephemeral; moving to tables / incremental tables as required for performance. ### Model attribute layout - Any id columns should be listed first in a model (model key first, followed by any foreign keys) - Any created/modified dates should be listed last in a model, followed by metadata fields (Fivetran, AirByte, etc) - All other fields in the model should be listed alphabetically, as contents may change over time. ![dbt column ordering: IDs first, attributes alphabetically, dates next, metadata fields last](./assets/dbt-std1.png) --- // File: best-practices/dbt/README # Overview As any data environment grows, it can become difficult for new team members to unravel complex pieces of logic within the project. To assist with future change & onboarding, each model should contain only what it must to get to the next stage of complexity. The [dbt project guidelines](/docs/best-practices/dbt/dbt-guidelines) assist with decision-making to keep the environment clean. ## Macros Any dbt macros created to apply repeated logic should be descriptively named with the action they accomplish: - add_load_timestamp_column - create_hash_key_from_columns - get_latest_record_for_key Macros in the *dbt-utils* package allow developers to develop in universal SQL syntax that can run on any modern database, giving the business flexibility to move between platforms as required in the future. Macros in *dbt-expectations* increases the testing suite significantly, including many statistical, multi-column, and aggregation tests. ## Tools & Packages One of the significant advantages of joining the global dbt community is the wide array of [open source libraries](https://datacoves.com/dbt-libs) and [dbt packages](https://hub.getdbt.com) we have available to us. These tools allow companies to move faster, produce more reusable and readable code, and make it far easier for those who come after us to keep moving forward. There are 3 primary types of tool: 1. Coding environment extensions and python libraries help find information quickly while developing data models, and make it easy to write good code quickly 2. Macros and packages extend the capability of SQL, by allowing reuse of advanced pieces of logic in a centralized, repeatable way. 3. Standards and expectations build trust in data and flag areas where the quality of code could be improved. Datacoves provides a web-based interface for AirByte, dbt, dbt Docs, Airflow, and Superset - streamlining the process of development by wrapping the below tools in a simple environment [dbt-coves](https://github.com/datacoves/dbt-coves) automates certain tasks such as creating source property(yml) files and initial staging models by querying the database eliminating tedious tasks. [sqlfluff](https://www.sqlfluff.com/) provides baseline expectations of clean code and flags many common logic issues as SQL is developed [dbt-checkpoint](https://github.com/dbt-checkpoint/dbt-checkpoint) helps perform governance checks like verifying the models have descriptions and table names are not hard coded --- // File: best-practices/dbt/object-naming # Object naming standards ## General Database Relation Naming As we build database objects for use across multiple areas, naming quickly emerges as the first (and arguably most important) method of documentation. It's an area of frustration for many developers, as it requires a context switch from "how do I make this code work" to thinking about future readers and how to make our models discoverable. While case sensitive names can be used in some data warehouses like Snowflake, they require double quotes to use and using mixed case introduces potential for duplication (`fieldName` and `FieldName` are different objects). We recommend `snake_case` for a consistent structure throughout our Snowflake databases. Similarly, all models managed by dbt must have unique names to allow references to be driven by the name of the table or view. As a general rule, names should use full words and increase in granularity left-right: `patient_address_city`. This assists with the sort order of our objects, grouping models and columns into obvious sections. ## Databases The warehouse should be separated into two primary parts: 1. The **raw** database where data gets ingested and which is not exposed to most users. 2. The **analytics** database is where the transformed data exists. This is where dbt operates. ## Raw Data ### RAW - Source database The **RAW_PRD** database is the landing area for integration tools such as Fivetran and Airbyte. A **RAW_DEV** database exists in parallel where new sources are added and tested before they are ready to be used in production. Sensitive data is restricted at the column level throughout the system: if sensitive data exists, the schema in the **raw** database is given a suffix of `_pii` to give users a hint that there is sensitive data in this schema. A corresponding security role must be assigned to a dbt developer to be able to see this data. Data flattening is managed with dbt in the `/models/inlets/` folder of the dbt repository. Flattened models must be created by developers permitted to see the sensitive data. These developers will flatten data to create more useable columns and they will apply masking and row level security rules. Developers can add new sources to **RAW_DEV** using Fivetran or AirByte. Any new data added to the raw database will not be immediately accessible by any user until proper permissions/roles created and granted. Only new and changed tables are created in **RAW_DEV**. All tables previously released to production will be available via dbt deferral from **RAW_PRD**. ## Transformed - Analytics Database ### ANALYTICS database This dbt-managed database contains all transformed models (inlets, bays, and coves) prepared for use by the business. Development and Test environments exist in parallel. **analytics_dev** is where new models are developed and **analytics_pr_``** is where the data is checked before changes are released to production. Security from **RAW_PRD** is automatically assigned to any views in the **ANALYTICS** database; the release process ensures the same rules are applied to any created tables as part of deployment. ### ANALYTICS_DEV database This database contains individual developer schemas where models are created and modified before they are ready to be deployed to production. All models are created here by developers using dbt within Datacoves. Each developer will have a schema named with their username as defined in the dbt profiles.yml. All models will build into the developer's schema even overriding the custom schema used in production. dbt will only create a models that are added or changed if the deferral feature is used. All developer schemas may be dropped at the start of every week to ensure sensitive data is not retained longer than necessary to comply with GDPR and other regulations. When creating models in Datacoves, any required data may be "deferred" from production: if the upstream models have not been changed, dbt can simply reference them from production rather than rebuilding them and increasing build time and duplication. This ensures that developers are always working on the freshest production data and reduces the likelihood of production failures. ### ANALYTICS_PR_`` databases Every time a pull request is opened from a feature branch to a release branch or from a feature / release branch to the main branch, a new database is automatically created to run and test the changes in that branch. By leveraging dbt's deferral and Slim CI (`state:modified`) features, we only build changed models and their downstream dependencies. Deferral allows us to pull unchanged upstream models from production. These databases are used for UAT as needed and act as "pre-release" for any manual review required to trust the new codebase before it is merged to the main branch. ![naming_databases](./assets/naming_databases.png) ## Data Flow The general flow of data is from Raw schemas to Inlets then Bays, and finally Coves. These will be described in more detail below. :::note See [this page](/docs/best-practices/dbt/inlets-bays-coves) to learn more about Inlets, Bays, and Coves ::: ![inlets-bays-coves](./assets/inlets-bays-coves.png) ## Raw Database Schemas The **RAW_PRD** database is primarily populated directly from vendor / source system data, and exists as a mirror in place of direct connection to those sources. Schemas are named as follows: `_`. This can be configured in Fivetran / AirByte (Airbyte calls schemas Namespaces). Source tables keep the name given by the source system. In large implementations where a source is unique to a specific country, the schema should be named as follows: `__` for clarity. ### Raw Database Tables To account for schema drift, all data should be loaded into VARIANT (semi-structured) columns directly in the database. You should create additional tables for nested objects as follows: `_[_]` with columns matching the keys of the original source. Schema name is included in order to avoid duplication where multiple source systems include identically-named tables ('User', 'Customer', etc). By keeping this source-driven convention, these can be created quickly by a technical team without needing to understand the subject matter. ### Source Connections A source connection configuration should be given the name of the source itself allowing clear visibility of where the data comes from without the need to open a connection configuration screen. ## Transformed - Analytics Schemas ![naming_schemas](./assets/naming_schemas.png) ### Inlets Inlet Schema names should match those in the RAW database for traceability. The first step in making raw data usable is to flatten and do some basic cleanup of the data. In dbt we use the inlets folder and we mirror the schema name created in the raw database. Here we do some basic transformation including: - flattening - aliasing columns for clarity - casting - cleansing (like converting all time stamps to UTC) ### Bays - Schemas The Bay schemas in the ANALYTICS database are named for data domains / entities of the business. These are intended for reuse in many areas. They are developed by cross-functional teams responsible for their ongoing development, so time should be taken to understand the subject and potential use cases. As the primary developers are still technical, there will be a natural leaning toward system-centric names - this should be challenged in code review, as this is our main chance to translate data from what a vendor cares about ("User") to what we actually care about ("Employee", "Customer", "Events"). Names should be: 1. verbose 2. generally increase in complexity left-right 3. un-repeating within a database → schema → model → column structure where practical The primary goal is searchability; while the structure in `_analytics.bay_customer.dim_customer.first_name_` breaks principles 2 and 3, renaming the dimension (`dim_customer`) or column (`name_first`) would make it harder to find and understand. ### Bays - Tables And Fields All models in a Bay schema should describe their modelling style as the first segment of the name: `dim_`, `fct_`, `ref_` etc. If any aggregation is required in a bay, this should be a suffix (`fct_direct_sales_month`) to group alongside other `direct_sales` facts. This immediately prepares the Cove developer (who are also technically skilled) with understanding of the join types they will need to query the object. Any ephemeral or staging models which aren't intended for use outside the Bay should be prefixed `int_` to show their intermediate/internal nature. Any static CSV data seeded from the dbt repository can be loaded directly to the appropriate name (e.g. `dim_date`). ### Coves The Cove schemas in **ANALYTICS** are named for specific use cases and analytic areas. These are built with less focus on reuse and a much greater focus on the experience for the user that will be consuming the data. All naming in a cove should be focused on use by less technically skilled users and tools, especially if the data is intended for self service. In many visualization tools it's easy to confuse `patients.name` with `products.name`, so we must include the table name as `patients.patient_name` in models. ![naming_fields](./assets/naming_fields.png) ![naming_fields2](./assets/naming_fields2.png) Where a modelling methodology has been used, prefixes should be used to describe models: `dim_`, `fct_`, etc. Any single-table analyses should be prefixed `mart_` and any models not intended for ongoing use should be prefixed `temp_`. End users may create their own models in their respective cove. These models should be prefixed with the username of the person who created them if not intended for general use: `_`. Any aggregation should be described in a suffix on the model/column: `customer_countries.customer_count`. --- // File: best-practices/dbt/inlets-bays-coves # What are Inlets, Bays & Coves Before companies start creating data warehouses or data lakes, they typically run their business with spreadsheets. Different areas of the business do analysis by combining different data sets to produce the metrics they need. An Accounts Payable analyst may have a file containing vendors, another containing invoices, and another containing purchase orders. With a few files this person can do their job. However, as the volume of data increases, Excel becomes unusable and these users are sent to gather the data they need from the data warehouse or data lake. Usability suffers and the organization becomes less agile. ## The Datacoves approach We want to give users an area where they can find the things they need to do their job, tailored to their use cases. These are `Data Coves`. In practical terms, they are schemas in a data warehouse that are tailored for a specific area of the business, in the example above, we would have an accounts_payable cove. ## Data Flow A Data Cove is a user centric data area, but data doesn't start out this way. Companies receive data that needs to be cleansed and harmonized. Before data is made available to end users, we should also assure that some level of data quality checks are performed. This is what happens in the **inlets** and **bays**. These areas could also be referred to as "staging" and "core". ![inlets-bays-coves](./assets/inlets-bays-coves.png) ## Raw This area mirrors the source system. It is where data is loaded into the warehouse. Raw data feeds **Inlets**. ## Inlets This area mirrors the raw database and it is where basic transformations such as renaming of columns, casting data to the proper data type, and harmonizing timezones is done. Inlets feed **bays**. ## Bays Bays are where we build *reusable* Data Products that adhere to Data Mesh principles. These should be thought of as business objects that do not change over time. While systems like a CRM may change, objects like Customers, Vendors, Employees, Orders, Online Interactions, etc remain constant over time. Bays feed the analytic **Coves**. ## Coves A cove is an analytics centric area focused on usability. It also adheres to Data Mesh principles. While a Bay may have a model for a customer dimension that may have hundreds of columns, when we expose that model in a cove we take into consideration which of those columns are needed by the given analytics area. Continuing from the example above, an accounts payable analyst that sees what is available in their accounts_payable cove should immediately be able to use the data just as they traditionally used their excel datasets. Their view of a vendor would not have several addresses for the vendor, but instead would have the address that accounts payable needs for their analytics. The diagram below illustrates how data flows between each layer and what takes place in each. ![inlets-bays-coves2](./assets/inlets-bays-coves2.png) ## Building Bays and Coves As stated above, Bays and Coves should adhere to Data Mesh principles and should have clear ownership. Data products are created and the complexity that goes into building them is encapsulated for future modification. The diagram below shows the qualities of data products. ![data-products](./assets/data-products.png) --- // File: best-practices/git/README # Git Overview While many see the advantage of dbt as a platform with unit testing, SQL for everything, and automatic documentation, these have all been available (to various extents) in data platforms for some time. The core innovation that dbt brought to DataOps is Context - the Transformation engine understands the relationship between models. In the old world, we designed pipelines - once a specific task has finished, the next can start - all chained together manually with plenty of 'padding' between scheduled start times. With dbt, the transformation tool itself knows what is required before any individual task can begin; dynamically executing a unique run order on every execution as each task has its dependencies resolved and can begin to run. This network of dependencies is the core competency of our platform, and allows several efficiencies: refreshing just what's needed on a data load, checking that downstream models ran as expected after a change, and reloading just what changed during a release. To assemble this network of dependencies, the first step dbt performs is to parse all code and to determine relationships between models. This happens whether the models are stored in the current repository or imported via shared packages. The specific models that are executed in the database will change based on the command (and access rights of the user); but the tool is always aware of the whole ecosystem. At end of sprint in a multiple-repository environment, code review and deployment must be synchronized: a raw database must be changed simultaneously with the downstream outputs. This becomes especially complex in the case of a hotfix to a well-used raw model, where every downstream Cove or area specific repository would need to be simultaneously released. The same models would run in either case; but a single repository allows a single review and merge process. ## Project Structure - Monorepo vs Multiple repositories A single repository manages subject areas as a thoughtful folder structure, each containing the logic and metadata required to create and document its models. Code review occurs within a project team before merging to a release for logic and security review. A single set of permissions can be released and enforced across the ecosystem alongside changes to logic. In a single-repository environment, security can be included alongside the logic itself, and is included as part of the codebase. This is more difficult in a multiple-repository environment, as there is no primary master (or if consolidating to one, it must be updated when any other environment changes) Below we outline the pros and cons to having a single repo vs having multiple dbt repositories. We recommend everyone start with a single repository based on the added complexity that having multiple repositories introduces. Only after careful consideration should you embark on managing multiple dbt repositories. ![mono-multiple-repos-1](./assets/git-st2.png) ![mono-multiple-repos-2](./assets/git-st1.png) If anyone challenges the viability of using a single repo for your project, show them the image below of the Linux project. ![linux-stats](./assets/linux-stats.png) ## Security in a monorepo While developers can view code across the wider organization, any attempts to execute that code to gain access to information are restricted by individual database permissions. Proposed changes to these objects or permissions are only executed with escalated permissions once the code has passed review; further protecting the environment from unauthorized access. ## Git Branching Strategy The concepts of branching, merging, and pull requests can be initially hard to grasp. As developers, many of us learned to code by hacking together solo projects - the rigour of organizational source control can feel like hard work. The speed of releases has gotten faster, but without proper care the environment can become very messy and untested code can break user trust in the data platform. To strike a balance between trust and efficiency, we follow a Release plan as illustrated in the diagrammed below. ![Git branching strategy diagram showing feature branches merging to a release branch before reaching main, with a two-phase development cycle](./assets/git-st3.png) The Main branch stores the current version of the truth in our Production database and will always describe exactly what has been run to update the production database. Data & Analytic Engineers complete analysis and explore potential new models by creating a *feature* branch off the *main* branch. Feature branches are further developed and unit tests are added to ensure the output can be trusted by the business. Once the Product Owner is satisfied with the delivery, a pull request is created to a *release* branch. In this pull request, the automated deployment process creates a fresh UAT database, and business users review the output against expectations before approving release of the candidate to the Release branch. Once code is reviewed and UAT is approved, the merge request is completed to the *release* Branch. A final set of integration tests is run before the Release Manager performs a deployment/merged to Main. --- // File: best-practices/snowflake/security-model # Snowflake Security Model ## Overview The right Security model is critical to ensuring sensitive data is protected. While Snowflake allows convenient swapping between granted roles during a single user session, datasets must be accessible to the same role in order to analyze them together. To create a seamless user experience, the user should be able to access all data required to operate using a single role. That same role may be granted to others in a users' direct team, but will differ between teams across the organization: each allowing access to their required data and environments. In Snowflake there are three options for how security is managed as seen in the image below. *Option 1* combines the access and the users into a single role. This is what most tool documentation will tell you do to as it is "simple". You create one role and give it the permissions it needs then grant users that role. The problem with this approach is that it is hard to scale and users either get too much or not enough access. You will find yourself repeating permissions across roles and when those permissions need to be updated you need to update them in many places. Simply put, this approach is not [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). In *Option 2* we create more granular roles and create a hierarchical structure where Sysadmin in this case has access to everything. While better than Option 1, this gives administrators access to data which they may not need to have. If a user needs to have access to data, they should have a role specifically for that, like an Analyst role. *Option 3* This is our recommended approach because this allows us to create granular permissions such as a role that unmasks masked data, or a role that gives access to a specific schema. We then combine those "Object" roles into "Functional" roles which are then granted to users. This allows us to have roles like _Analyst_ and _Analyst\_PII_ with the only difference being that the latter gets an additional role that unmasks data. More importantly, the role to unmask data is defined once and used many times. ![Diagram comparing three Snowflake role-based access control models: Option 1 Combined Role, Option 2 Nested Roles, and Option 3 Composite Role](./assets/db-auth-std1.png) As shown below we create granular object roles each allowing access to a single database, schema, warehouse (compute cluster), Data classification, and region/country. We then grant the full set of required object roles to a given functional role which are in turn granted to users to allow them to query the database. ![Table comparing Snowflake composite role components across three functional roles: Data Engineer, PII Data Engineer, and PII Power User Germany](./assets/db-auth-std2.png) This approach allows clear visibility of what a given user has access to: Production or Development environments, Raw or Processed schema, PII / Non-PII, etc. Any newly required permissions can be added for a team via its user role easily, by simply granting an additional object role. These permissions are all defined in source files which are then leveraged by a tool called Permifrost. This adds traceability to the role management process and can be run as part of the DataOps process. This process will also retain a record of who made the change, when they changed it, and their justification for granting / revoking access. ## PII Access ![Four-step flow describing how Snowflake dynamic masking policies protect PII data across raw databases, derived tables, dev schemas, and newly developed features](./assets/db-auth-std3.png) In order to analyze our most secure data, we must first protect it - allowing access to those who have been appropriately trained to meet our compliance and quality standards. While Snowflake allows many complex access policies, we take care to apply what is needed to meet our compliance obligations while retaining the ability to understand the data and create value for our users. PII data is protected within marked columns by using Dynamic Masking policies: predetermined methods for protecting all or part of the data unless a certain access role is granted, which can be applied to any column in the database. This protection may be complete masking ('\*\*\*\*') or partial masking ('\*\*\*\*\*@gmail.com', 'Martin \*\*\*\*\*\*'), and can use any SQL methods to determine the content of masked data to a given role. ![Table showing masked PII data with fully masked Info VIP column and partially masked email_address column](./assets/db-auth-std4.png) In this implementation, we can mask PII data and have the ability to expand this to other sensitive data over time as required. PII and other sensitive data are then only accessible by users with a role that can unmask this data. As developers work in their own dev schemas, any data saved to a table in their schema will contain only the level of information they are permitted access to; and will only be accessible by users of the same role. Before release of their newly developed feature, any created tables are expected to be given security configuration, protecting any derived data to the same standard as its source. --- // File: best-practices/snowflake/time-travel # Snowflake Time travel & GDPR In order to comply with GDPRs right to be forgotten, Time Travel for PII data is set at 7 days for Production data, and 21 days for snapshots. Time Travel for non-PII is set to 30 days as default. ![time-travel-retention](./assets/time-trvl1.png) Production retention period is set up as part of our initial database creation and is implemented with the below settings: - **ANALYTICS** database: ``` sql alter database analytics set data_retention_time_in_days = 30 ``` - **ANALYTICS_DEV** database: ``` sql alter database analytics_dev set data_retention_time_in_days = 7 ``` - Macro created to run on-run-end: This loops through all models in database at the end of every production run, and checks for the pii metadata flag. - If found: - for snapshots: `set alter table < table name > set data_retention_time_in_days = 21` - for pii tables `set alter table < table name > set data_retention_time_in_days = 7` The default table materialization causes issues for Time Travel by dropping the table on every refresh. You will need to create a Time Travel specific table materialization that only recreates the table when the columns have changed. This Time Travel table materialization should be used in place of table materialization throughout the environment. >[!TIP]Example materialization can be found [here](https://github.com/edx/snowflake_timetravel_table/blob/main/macros/snowflake_timetravel_table.sql) ## Time Travel Data Lifecycle When a request for a person "to be forgotten" from the database comes in, the following set of events occur. 1. 5 days are given for request to make it to the appropriate team so they can remove the record from the **RAW* database 2. The production **ANALYTICS** database is refreshed on the next daily dbt run clearing out offending records. This database has a 7 day retention period on PII tables, so that history will be removed at that time. 3. Non-PII data will not be impacted and remain in the system with the standard 30 day retention period. 4. Since developers may have PII data in their schemas, developers schemas are dropped every 7 days and their retention period is set to 7. ![time-travel-data-lifecycle](./assets/time-trvl2.png) --- // File: best-practices/snowflake/comparing-tables # Comparing Tables Across Environments ## Overview When migrating data pipelines (e.g., from Streamsets to dlt), it is critical to verify that the new pipeline produces identical data to the original. This guide documents best practices and common pitfalls for comparing tables across Snowflake databases, based on real-world experience comparing production (PRD) and sandbox (SBX) environments. ### How Data is Stored In our setup, each pipeline extracts data from a source system and writes it as JSONL files to S3. These files are then loaded into Snowflake using the `COPY INTO` command. Each row in the target table contains an `object_data` column of type `VARIANT` that holds the full JSON payload for that record. The comparison techniques in this guide operate on this `object_data` column — hashing it, parsing it, and comparing its keys and values across environments. ### Why a Single JSON Column Storing the entire record as a single `VARIANT` column rather than mapping each field to its own Snowflake column has several advantages for the raw/landing layer: - **Schema flexibility**: Source schemas change frequently — new columns appear, old ones are renamed or removed. A single JSON column absorbs these changes without requiring `ALTER TABLE` statements or pipeline redeployment. The raw layer never breaks due to upstream schema drift. - **Simplified loading**: `COPY INTO` with a single VARIANT column is a straightforward, universal pattern that works for any source system. There is no need to maintain column mappings or type casts at the loading stage. - **Full-record hashing**: Comparing entire rows across environments becomes a single `MD5(object_data)` call. With separate columns, you would need to concatenate or hash each column individually, handle NULLs, and deal with column ordering — all of which introduce edge cases. - **Auditability**: The raw JSON is preserved exactly as it was extracted. Downstream transformations (in dbt or similar tools) parse the JSON into typed columns, but the original payload remains available for debugging and reprocessing. - **Decoupled extract and transform**: The extraction layer only needs to get data into Snowflake reliably. All type casting, column naming, and business logic happens in the transformation layer, keeping each stage simple and independently testable. ### Table Structure Assumptions The queries in this guide assume each raw table has the following columns: | Column | Type | Description | |--------|------|-------------| | `object_data` | `VARIANT` | The full JSON payload for a single record, loaded from a JSONL file via `COPY INTO`. | | `pipeline_start_utc` | `TIMESTAMP` | The UTC timestamp of when the pipeline run started. Every row loaded in the same pipeline execution shares the same value. This acts as a batch identifier — it allows you to isolate the latest load and compare it against the corresponding load on the other side. | These columns are added automatically by the loading framework. The `pipeline_start_utc` column is essential for comparison because tables accumulate data from multiple pipeline runs, and you typically want to compare only the most recent run on each side. ### Prerequisites To run cross-database comparison queries (e.g., `MINUS` between PRD and SBX), both databases must be accessible from the same Snowflake connection. This requires setting up a database link or share from the production account to the sandbox account. For example, if your production data lives in `PRD_RAW` and sandbox data in `SBX_RAW`, both databases must be queryable from a single session so you can use fully qualified names like `PRD_RAW.MY_SCHEMA.MY_TABLE` and `SBX_RAW.MY_SCHEMA.MY_TABLE` in the same query. If PRD and SBX are on separate Snowflake accounts, you can create a database in the PRD account that points to the SBX data (e.g., via Snowflake data sharing or a replicated database), allowing cross-database queries without switching connections. ## Comparison Strategy A layered approach works best, progressing from cheap/fast checks to expensive/thorough ones. Each layer acts as a fast-fail gate — skip deeper checks if earlier ones fail. ### Layer 1: Row Count The simplest sanity check. Compare the number of rows for the latest pipeline run on each side. Run these two queries and compare the results: ```sql -- Count rows for the latest pipeline run in PRD SELECT 'PRD' AS source, COUNT(*) AS row_count FROM PRD_RAW.MY_SCHEMA.MY_TABLE WHERE pipeline_start_utc = ( SELECT MAX(pipeline_start_utc) FROM PRD_RAW.MY_SCHEMA.MY_TABLE ); -- Count rows for the latest pipeline run in SBX SELECT 'SBX' AS source, COUNT(*) AS row_count FROM SBX_RAW.MY_SCHEMA.MY_TABLE WHERE pipeline_start_utc = ( SELECT MAX(pipeline_start_utc) FROM SBX_RAW.MY_SCHEMA.MY_TABLE ); ``` If both tables have zero rows, skip all remaining checks. If row counts differ, investigate before proceeding — deeper checks will be misleading if the data volume is fundamentally different. :::note Row count equality does not guarantee data equality. Two tables can have the same number of rows with completely different content. ::: ### Layer 2: JSON Structure (Key Count, Case Sensitivity, Types) Fetch a representative row from each side and compare the JSON structure of the data column (e.g., `object_data`): - **Key count**: Do both rows have the same number of top-level keys? - **Case sensitivity**: Are there keys that differ only in casing (e.g., `firstName` vs `FirstName`)? - **Missing keys**: Keys that exist on one side but not the other. - **Value types**: For shared keys, do the value types match? :::warning When comparing JSON structures, ensure you are comparing the **same record** on both sides. Fetching the "latest row" independently from each table may return completely different records, leading to false structural differences. Use a scored matching approach (described below in "Finding the Matching Row") to find the corresponding row. ::: ### Layer 3: Sample Hash Verification Take a sample of rows from one table, compute their content hash, and check if those hashes exist in the other table. This catches most mismatches without scanning the full table. ```sql -- Step 1: Get 1000 distinct hashes from PRD WITH prd_sample AS ( SELECT DISTINCT MD5(object_data) AS row_hash FROM PRD_RAW.MY_SCHEMA.MY_TABLE WHERE pipeline_start_utc = ( SELECT MAX(pipeline_start_utc) FROM PRD_RAW.MY_SCHEMA.MY_TABLE ) LIMIT 1000 ) -- Step 2: Check how many of those hashes exist in SBX SELECT (SELECT COUNT(*) FROM prd_sample) AS prd_sample_count, COUNT(s.row_hash) AS found_in_sbx FROM prd_sample p LEFT JOIN ( SELECT DISTINCT MD5(object_data) AS row_hash FROM SBX_RAW.MY_SCHEMA.MY_TABLE WHERE pipeline_start_utc = ( SELECT MAX(pipeline_start_utc) FROM SBX_RAW.MY_SCHEMA.MY_TABLE ) ) s ON p.row_hash = s.row_hash; ``` If `found_in_sbx` equals `prd_sample_count`, all sampled rows match. Otherwise, investigate the missing hashes. :::note Use `DISTINCT` on the hash to avoid counting duplicate rows as mismatches. Tables may contain legitimate duplicate rows that inflate the sample count. ::: ### Layer 4: Full Hash Comparison (MINUS) Only run this after the sample check passes (to fast-fail on obvious mismatches). Use Snowflake's `MINUS` operator to find rows that exist on one side but not the other: ```sql -- Rows in PRD that are not in SBX SELECT MD5(object_data) AS row_hash FROM PRD_RAW.MY_SCHEMA.MY_TABLE WHERE pipeline_start_utc = ( SELECT MAX(pipeline_start_utc) FROM PRD_RAW.MY_SCHEMA.MY_TABLE ) MINUS SELECT MD5(object_data) AS row_hash FROM SBX_RAW.MY_SCHEMA.MY_TABLE WHERE pipeline_start_utc = ( SELECT MAX(pipeline_start_utc) FROM SBX_RAW.MY_SCHEMA.MY_TABLE ); ``` Run the reverse direction as well (SBX MINUS PRD) to catch rows only in SBX. For cross-database comparisons, always use fully qualified table names (`database.schema.table`). ## Common Pitfalls ### JSON Serialization Differences Different loaders serialize JSON differently. Two records with identical data can produce different `MD5(object_data)` hashes due to: - **Key ordering**: `{"a":1,"b":2}` vs `{"b":2,"a":1}` - **Number formatting**: `0.0` vs `0`, or `33.99831` vs `33.998310000000004` - **Nested object stringification**: `{"key": {"sub": 1}}` vs `{"key": "{\"sub\": 1}"}` - **Null representation**: A key with `null` value vs the key being absent entirely When hash mismatches occur but parsed values are identical, the difference is purely in serialization. Use Snowflake's `HASH` function on the parsed JSON for a normalized, order-independent comparison: ```sql -- Normalized comparison (slower, ignores JSON formatting differences) SELECT TO_VARCHAR(HASH(PARSE_JSON(object_data))) AS normalized_hash FROM PRD_RAW.MY_SCHEMA.MY_TABLE WHERE pipeline_start_utc = ( SELECT MAX(pipeline_start_utc) FROM PRD_RAW.MY_SCHEMA.MY_TABLE ); ``` This is slower than `MD5` because it parses every row's JSON, but it eliminates false positives from serialization differences. ### Finding the Matching Row When a hash mismatch is found, you need to locate the corresponding row on the other side to understand what changed. A score-based approach works well — build a `CASE` expression for every column and return the row with the highest number of matching columns: ```sql -- Given a PRD row with values col1='ABC', col2='123', col3='XYZ' -- find the best matching row in SBX SELECT object_data, ( CASE WHEN object_data:"col1"::STRING = 'ABC' THEN 1 ELSE 0 END + CASE WHEN object_data:"col2"::STRING = '123' THEN 1 ELSE 0 END + CASE WHEN object_data:"col3"::STRING = 'XYZ' THEN 1 ELSE 0 END ) AS match_score FROM SBX_RAW.MY_SCHEMA.MY_TABLE WHERE pipeline_start_utc = ( SELECT MAX(pipeline_start_utc) FROM SBX_RAW.MY_SCHEMA.MY_TABLE ) ORDER BY match_score DESC LIMIT 1; ``` :::tip Compare all columns as `STRING` to avoid type cast errors. For example, a date stored as `'2026-03-12'` on one side and as an epoch integer on the other will fail a `::NUMBER` cast. ::: ### Floating Point Precision When extracting `DECIMAL`/`NUMERIC` columns through connectors that use `float64` internally (like ConnectorX), precision drift can occur: | Source Value | float64 Representation | |-------------|----------------------| | `33.99831` | `33.998310000000004` | **Fix**: In the extraction query (run against the **source database**, not Snowflake), cast the column to `FLOAT` with `ROUND` to the original scale. The scale can be detected from the source's metadata. For example, in MSSQL: ```sql -- Run on the source MSSQL database: detect the scale for DECIMAL/NUMERIC columns SELECT COLUMN_NAME, NUMERIC_SCALE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'my_table' AND DATA_TYPE IN ('decimal', 'numeric'); -- Then apply ROUND in the extraction query (also on the source database) SELECT ROUND(CAST(my_column AS FLOAT), 5) AS my_column -- 5 = NUMERIC_SCALE FROM dbo.my_table; ``` ### Timestamp and Timezone Handling Timestamp conversion is the most common source of data mismatches between different extraction tools. **Understanding the problem**: Many source databases store timestamps without timezone information (naive timestamps). When converting these to epoch milliseconds (UTC), the extraction tool must assume a timezone. Different tools may apply this conversion differently, especially around Daylight Saving Time (DST) transitions. **DST Spring-Forward**: During the spring-forward transition (e.g., last Sunday of March in Europe), clocks jump from 02:00 to 03:00. A timestamp at 01:00 CET is unambiguous, but checking its timezone offset against the offset one hour later (which is now CEST) reveals a change. Some implementations incorrectly add a 1-hour correction here. The fix: only apply the correction during **fall-back** (last Sunday of October), when the timezone offset decreases (e.g., from CEST +02:00 back to CET +01:00). During fall-back, the hour between 02:00 and 03:00 repeats, creating genuine ambiguity. During spring-forward, there is no ambiguity — the offset increases but no correction is needed. In MSSQL (run against the **source database** as part of the extraction query), you can detect this by comparing the timezone offset of the value against the offset of the value plus one hour: ```sql -- MSSQL source query: correct — only trigger when timezone offset decreases (fall-back) CASE WHEN DATEPART(TZOFFSET, CAST(my_col AS DATETIME2) AT TIME ZONE 'W. Europe Standard Time') > DATEPART(TZOFFSET, CAST(DATEADD(HOUR, 1, my_col) AS DATETIME2) AT TIME ZONE 'W. Europe Standard Time') THEN 3600000 ELSE 0 END -- MSSQL source query: wrong — triggers on any timezone offset change (including spring-forward) CASE WHEN DATEPART(TZOFFSET, CAST(my_col AS DATETIME2) AT TIME ZONE 'W. Europe Standard Time') != DATEPART(TZOFFSET, CAST(DATEADD(HOUR, 1, my_col) AS DATETIME2) AT TIME ZONE 'W. Europe Standard Time') THEN 3600000 ELSE 0 END ``` Here, `DATEPART(TZOFFSET, ...)` returns the UTC offset in minutes for a timezone-aware value. During fall-back the offset goes from 120 (CEST) to 60 (CET), so `120 > 60` is true and the correction applies. During spring-forward the offset goes from 60 to 120, so `60 > 120` is false and no correction is applied. **Oracle-specific pitfalls**: Oracle's `FROM_TZ` function, which assigns a timezone to a naive timestamp, has additional edge cases: - **Pre-1900 dates** (sentinel values like year 1111): Oracle uses historical Local Mean Time (LMT) for named timezones. For `Europe/Brussels`, LMT is `+0:17:30`, not `+01:00`. But JDBC/Java uses CET (`+01:00`) for all historical dates. This 43-minute difference causes mismatches. Fix: use a fixed offset `'+01:00'` instead of the named timezone for dates before 1900. - **Far-future dates** (sentinel values like year 9621): Oracle's timezone data has a limited range. Beyond ~2100, `FROM_TZ` falls back to standard time regardless of the month. But Java extrapolates DST rules forever. Fix: apply the offset manually — use `'+02:00'` (CEST) for April–September and `'+01:00'` (CET) for October–March. - **Normal dates (1900–2100)**: Use the named timezone (e.g., `'Europe/Brussels'`) and let Oracle handle DST transitions, which matches Java behavior. ```sql -- Oracle source query: timezone selection with sentinel value handling FROM_TZ( CAST(my_col AS TIMESTAMP), CASE WHEN my_col < DATE '1900-01-01' THEN '+01:00' WHEN my_col > DATE '2100-01-01' THEN CASE WHEN EXTRACT(MONTH FROM my_col) BETWEEN 4 AND 9 THEN '+02:00' ELSE '+01:00' END ELSE 'Europe/Brussels' END ) AT TIME ZONE 'UTC' ``` **DATE vs DATETIME**: Columns stored as `DATE` (no time component) should be converted to epoch milliseconds at midnight in the target timezone. JDBC-based tools like Streamsets do this automatically; custom pipelines must handle it explicitly. In Oracle specifically, `DATE` columns store both date and time, but some connectors (like ConnectorX) may read them as date-only, losing the time component. Detect these from `ALL_TAB_COLUMNS` metadata and force a `TIMESTAMP` cast. ### Null Column Handling Different extraction systems handle null columns differently: - **Streamsets** creates columns for the union of all fields across all records, filling `null` for records that don't have a given field. - **dlt** by default strips keys with null values during JSON serialization. - **Elasticsearch** returns only the fields that exist in each document. When columns are always null (they only exist in the schema, never populated in the data), they may be stripped by the new pipeline. This leads to "only in PRD" differences where every missing column has a value of `None`. Solutions include declaring expected columns via schema hints, patching the serializer to preserve null values, or collecting all keys across all records and backfilling `null` for missing ones. ### Nested Object Flattening Different systems flatten nested JSON in incompatible ways: | System | Input | Output | |--------|-------|--------| | Streamsets | `{a: {b: 1}}` | `a.b: 1` (dot-separated) | | Streamsets | `{a: [{b: 1}, {b: 2}]}` | `a.0.b: 1, a.1.b: 2` (indexed arrays) | | dlt (max_table_nesting=0) | `{a: {b: 1}}` | `a: "{\"b\": 1}"` (stringified) | | dlt (default) | `{a: {b: 1}}` | Separate child table | When migrating from Streamsets and the downstream models depend on the dot-notation column names, custom flattening logic may be needed to match the expected format with indexed arrays. ## Recommended Comparison Workflow 1. **Row count** first — skip everything if both sides have zero rows 2. **JSON structure** — compare key counts, names, and types on matched rows 3. **Sample verification** (1000 rows) — fast-fail on hash mismatches 4. **Full MINUS comparison** — only if sampling passes 5. **Row-level diff** — for mismatched rows, use scored matching and column-by-column comparison 6. **Raw JSON diff** — when parsed values match but hashes differ, compare the raw JSON strings to identify serialization differences --- // File: best-practices/vscode/README # VS Code Best Practices and Tips --- // File: best-practices/vscode/tips # VS Code Tips and tricks ## Shortcuts - `cmd + alt + Shift + L` ➡️ Select all occurrences of a selected phrase --- // File: getting-started/create-account # Configure your account with Datacoves :::caution The appropriate git repo access is required to be able to add deployment keys. Be sure that you can add SSH keys to the repo or setup will not be able to finish. ::: ## Prerequisites Before the setup call with the Datacoves team, ensure you have the following ready. :::note Email gomezn@datacoves.com with the answers to the following two questions so we can be ready for the call. ::: 1. What version of dbt are you using? 2. Do you use Google / Google Workspace or Microsoft to authenticate? Datacoves leverages your existing authentication service. ### Data Warehouse To set up your Datacoves account, you will need to know your data warehouse provider and have relevant access details handy. This includes the service account that Airflow will use. | Data Warehouse Provider | Information Needed | | --- | --- | | Snowflake | Account, Warehouse, Database, Role, User, Password, Schema | | Redshift | Host, Database, User, Schema, Password | | Databricks | Host, Schema, HTTP Path, Token | | BigQuery | Dataset, Keyfile JSON | :::warning For the Snowflake `Account` field you will need to find your account locator and replace `.` with `-`. Check out [Snowflake Fields](/docs/how-tos/datacoves/how_to_connection_template#for-snowflake-the-available-fields-are) on how to find your Snowflake account locator. ::: **Network Access:** Verify that your Data Warehouse is accessible from outside your network. You'll need to whitelist the Datacoves IP - `74.179.200.137` ### Git To configure the git integration, you will need: **Git Access** Ensure your user has access to add a deployment key to the repo, as well as access to clone and push changes. **Git Repo** If this will be a new dbt project, create a new repo and ensure at least one file is in the `main` branch such as a `README.md`. Have your git clone URL handy. **dbt Docs** Create a `dbt-docs` branch in your repo. ## During Call with Datacoves To ensure a smooth call, please have the answers to the following questions ready to go. - What do you want to call your account? (This is usually the company name) - What do you want to call your project? (This can be something like Marketing DW, Finance 360, etc) - Do you currently have a CI/CD process and associated script like GitHub Actions workflow? If not, do you plan on creating a CI/CD process? - Do you need any specific Python library on Airflow or VS Code? (outside the standard dbt-related items) --- // File: getting-started/admin/configure-airflow # Configuring Airflow You don't need Airflow to begin using Datacoves, but at some point you will want to schedule your dbt jobs. The following steps will help you get started using Airflow. Keep in mind this is the basic setup, you can find additional Aiflow information in the how-tos and reference sections. 1. To complete the initial configuration of Airflow, you will need to make changes to your project. This includes creating the dbt profile for Airflow to use as well as the Airflow DAG files that will schedule your dbt runs. [Initial Airflow Setup](/docs/how-tos/airflow/initial-setup) 2. Airflow will authenticate to your data warehouse using a service connection. The credentials defined here will be used by dbt when your jobs run. [Setup Service Connection](/docs/how-tos/datacoves/how_to_service_connections) 3. Datacoves uses a specific [folder structure](/docs/best-practices/datacoves/folder-structure) for Airflow. You will need to add some folders and files to your repository for Airflow to function as expected. [Update Repository](/docs/getting-started/admin/configure-repository) 4. When Airflow jobs run you may want to receive notifications. We have a few ways to send notifications in Datacoves. Choose the option that makes sense for your use case. - **Email:** [Setup Email Integration](/docs/how-tos/airflow/send-emails) - **MS Teams:** [Setup MS Teams Integration](/docs/how-tos/airflow/send-ms-teams-notifications) - **Slack:** [Setup Slack Integration](/docs/how-tos/airflow/send-slack-notifications) ## Getting Started Next Steps Once Airflow is configured, you can begin scheduling your dbt jobs by [creating Airflow DAGs](/docs/getting-started/admin/creating-airflow-dags)! --- // File: getting-started/admin/creating-airflow-dags # Creating Airflow Dags ## Pre-Requisites By now you should have: - [Configured Airflow](/docs/getting-started/admin/configure-airflow) in Datacoves - [Updated your repo](/docs/getting-started/admin/configure-repository) to include `automate/dbt/profiles.yml` and `orchestrate/dags` folders - [Set up notifications](/docs/how-tos/airflow/send-emails) for Airflow ## Where to create your DAGs This means that Airflow is fully configured and we can turn our attention to creating DAGs! Airflow uses DAGs to run dbt as well as other orchestration tasks. Below are the important things to know when creating DAGs and running dbt with Airflow. During the Airflow configuration step you added the `orchestrate` folder and the `dags` folder to your repository. Here you will store your airflow DAGs. ie) You will be writing your python files in `orchestrate/dags` ## DAG 101 in Datacoves 1. If you are eager to see Airflow and dbt in action within Datacoves, here is the simplest way to run dbt with Airflow. [Run dbt](/docs/how-tos/airflow/dags/run-dbt) 2. You have 2 options when it comes to writing DAGs in Datacoves. You can write them out using Python and place them in the `orchestrate/dags` directory, or you can generate your DAGs with `dbt-coves` from a YML definition. [Generate DAGs from yml definitions](/docs/how-tos/airflow/dags/generate-dags-from-yml) this is simpler for users not accustomed to using Python 3. You may also wish to use external libraries in your DAGs such as Pandas. In order to do that effectively, you can create custom Python scripts in a separate directory such as `orchestrate/python_scripts` and use the `DatacovesBashOperator` to handle all the behind the scenes work as well as run your custom script.**You will need to contact us beforehand to pre-configure any python libraries you need.** [External Python DAG](/docs/how-tos/airflow/dags/external-python-dag) --- // File: getting-started/admin/configure-repository # Update Repository for Airflow Now that you have configured your Airflow settings you must ensure that your repository has the correct folder structure to pick up the DAGs we create. You will need to add folders to your project repository in order to match the folder defaults we just configured for Airflow. These folders are `orchestrate/dags` and, optionally, `orchestrate/dags_yml_definitions`. **Step 1:** Add a folder named `orchestrate` and a folder inside `orchestrate` named `dags`. `orchestrate/dags` is where you will be placing your DAGs as defined earlier in our Airflow settings with the `Python DAGs path` field. **Step 2:** **ONLY If using Git Sync**. If you have not already done so, create a branch named `airflow_development` from `main`. This branch was defined as the sync branch earlier in our Airflow Settings with the `Git branch name` field. Best practice will be to keep this branch up-to-date with `main`. **Step 3:** **This step is optional** if you would like to make use of the [dbt-coves](https://github.com/datacoves/dbt-coves?tab=readme-ov-file#airflow-dags-generation-arguments) `dbt-coves generate airflow-dags` command. Create the `dags_yml_definitions` folder inside of your newly created `orchestrate` folder. This will leave you with two folders inside `orchestrate`- `orchestrate/dags` and `orchestrate/dags_yml_definitions`. **Step 4:** **This step is optional** if you would like to make use of the dbt-coves' extension `dbt-coves generate airflow-dags` command. You must create a config file for dbt-coves. Please follow the [generate DAGs from yml](/docs/how-tos/airflow/dags/generate-dags-from-yml) docs. ## Create a profiles.yml If the `delivery mode` of your service connection is [`Environment Variables`](/docs/how-tos/datacoves/how_to_service_connections) then Airflow will need a `profiles.yml`. The available environment variables will vary based on your data warehouse. We have made it simple to set this up by completing the following steps. This profiles.yml will also be used in the CI/CD process. To create your and your `profiles.yml`: **Step 1:** Create the `automate` folder at the root of your project **Step 2:** Create the `dbt` folder inside the `automate` folder **Step 3:** Create the `profiles.yml` inside of your `automate` folder. ie) `automate/dbt/profiles.yml` **Step 4:** Copy the following configuration into your `profiles.yml` ### Snowflake ``` yaml default: target: default_target outputs: default_target: type: snowflake threads: 8 client_session_keep_alive: true account: "{{ env_var('DATACOVES__MAIN__ACCOUNT') }}" database: "{{ env_var('DATACOVES__MAIN__DATABASE') }}" schema: "{{ env_var('DATACOVES__MAIN__SCHEMA') }}" user: "{{ env_var('DATACOVES__MAIN__USER') }}" password: "{{ env_var('DATACOVES__MAIN__PASSWORD') }}" role: "{{ env_var('DATACOVES__MAIN__ROLE') }}" warehouse: "{{ env_var('DATACOVES__MAIN__WAREHOUSE') }}" ``` ### Redshift ```yaml company-name: target: dev outputs: dev: type: redshift host: "{{ env_var('DATACOVES__MAIN__HOST') }}" user: "{{ env_var('DATACOVES__MAIN__USER') }}" password: "{{ env_var('DATACOVES__MAIN__PASSWORD') }}" dbname: "{{ env_var('DATACOVES__MAIN__DATABASE') }}" schema: analytics port: 5439 ``` ### BigQuery ```yaml my-bigquery-db: target: dev outputs: dev: type: bigquery method: service-account project: GCP_PROJECT_ID dataset: "{{ env_var('DATACOVES__MAIN__DATASET') }}" threads: 4 # Must be a value of 1 or greater keyfile: "{{ env_var('DATACOVES__MAIN__KEYFILE_JSON') }}" ``` ### Databricks ```yaml your_profile_name: target: dev outputs: dev: type: databricks catalog: [optional catalog name if you are using Unity Catalog] schema: "{{ env_var('DATACOVES__MAIN__SCHEMA') }}" # Required host: "{{ env_var('DATACOVES__MAIN__HOST') }}" # Required http_path: "{{ env_var('DATACOVES__MAIN__HTTP_PATH') }}" # Required token: "{{ env_var('DATACOVES__MAIN__TOKEN') }}" # Required Personal Access Token (PAT) if using token-based authentication threads: 4 ``` ## Getting Started Next Steps You will want to set up notifications. Selet the option that works best for your organization. - **Email:** [Setup Email Integration](/docs/how-tos/airflow/send-emails) - **MS Teams:** [Setup MS Teams Integration](/docs/how-tos/airflow/send-ms-teams-notifications) - **Slack:** [Setup Slack Integration](/docs/how-tos/airflow/send-slack-notifications) --- // File: getting-started/admin/configure-repository-using-dbt-coves # Initial Datacoves Repository Setup ## Introduction Setting up a new data project requires careful consideration of tools, configurations, and best practices. Datacoves simplifies this process by providing a standardized, yet customizable setup through the `dbt-coves` library. This article explains how to initialize and maintain your Datacoves repository. ## Getting Started with dbt-coves Setup The `dbt-coves setup` command generates a fully configured project environment tailored to your specific needs. This command creates a repository structure with all necessary components pre-configured according to data engineering best practices. ### Initial Setup Process dbt-coves comes pre-installed in Datacoves, you only have to run: ```bash # Create a new Datacoves repository dbt-coves setup ``` During the setup process, you'll be guided through a series of configuration questions that determine: - Which data warehouse to use (Snowflake, BigQuery, Redshift, Databricks) - Which components to include in your stack (dbt, Airflow, dlt) - Project naming conventions - Repository structure preferences - CI/CD pipeline configurations - Testing and documentation settings :::note It is recommended that you commit the answers file in your repo for future updates (see below) ::: ::: ## What Gets Created The `dbt-coves setup` command generates a comprehensive project structure that includes: 1. **dbt configuration** - Pre-configured dbt project with appropriate adapters - Custom macros tailored to your selected data warehouse - Template generators for consistent model creation 2. **Orchestration tools** - Airflow DAG templates (if selected) - Pipeline configurations 3. **Data loading** - dlt configurations for data ingestion (if selected) 4. **Quality control** - SQLFluff and YAMLlint configurations - dbt test frameworks - CI/CD workflows for GitHub Actions or GitLab CI 5. **Documentation** - README templates - Project structure documentation ## Customizing Your Setup The setup process is highly flexible, allowing you to: - Select only the components you need - Configure folder structures based on your preferences - Set up CI/CD pipelines appropriate for your workflow - Include specialized macros for your specific data warehouse ## Updating Your Repository As your project evolves or as Datacoves releases template improvements, you can update your existing repository: ```bash # Update an existing Datacoves repository dbt-coves setup --update ``` The update process: - Preserves your custom code and configurations - Updates template-managed files with the latest versions - Adds any new components you select (it will remove the components you selected at Setup time but didn't select at Update time) - Maintains backward compatibility where possible :::note When running an update, you will be prompted for the services you want to setup / update, if you saved the answers file from when you first ran set, your original choices pre-selected. If you unselect one of these, that content will be deleted ::: ::: ## Benefits for Data Teams This approach to repository setup and maintenance offers several advantages: 1. **Reduced setup time** from days to minutes 2. **Consistency** across projects and teams 3. **Built-in best practices** for data modeling and CI/CD 4. **Easy maintenance** through template updates 5. **Standardized testing** and quality control ## Conclusion The `dbt-coves setup` command streamlines the creation and maintenance of Datacoves repositories by providing a solid foundation that incorporates industry best practices. Whether you're starting a new data project or standardizing existing ones, this approach offers a scalable and maintainable solution for modern data stack implementation. By leveraging this setup process, data teams can focus on delivering value through data transformations and insights rather than spending time on infrastructure configuration. --- // File: getting-started/admin/user-management # User Management 1. To get your users up and running, you need to invite them to the platform and grant them access to projects or specific environments. [Invite Users](/docs/how-tos/datacoves/how_to_invitations) 2. You can change the permissions for users via the Users admin screen. [Edit Users](/docs/how-tos/datacoves/how_to_manage_users#edit-a-user) 3. When users no longer need access to Datacoves, you can delete their accounts. [Deleting a Users](/docs/how-tos/datacoves/how_to_manage_users#delete-a-user) --- // File: getting-started/developer/snowflake-extension # Getting Started with the Snowflake Extension ## This getting started video guide covers - Account sign in - Extension Interface overview - Object Exploration - Autocomplete - Running Queries For more information, please see the **[Snowflake VS Code Extension Docs](https://docs.snowflake.com/en/user-guide/vscode-ext)**
## When to Use This - You accidentally deleted a file that wasn't committed to Git. - You made changes to a file but didn't commit them and want to go back to an earlier version. - You closed a file after deleting it and need to recover its contents. ## Steps to Restore a File 1. Open the **Command Palette** (`Ctrl+Shift+P` / `Cmd+Shift+P`). 2. Search for **Local History: Find Entry to Restore**. 3. Type the name of the file you want to recover. 4. Select the snapshot you want to restore. If the file had multiple saves, you will see multiple snapshots to choose from. 5. In the history view, click the **checkmark icon** (✓) on the right side of the snapshot entry to restore the file. --- // File: how-tos/vs-code/datacoves-copilot/copilot import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; # Datacoves Copilot This section describes how to configure and use Datacoves Copilot, which comes installed on Datacoves v4+, enhancing the experience and supporting the following LLM providers: - Anthropic - Azure OpenAI - DeepSeek - Google Gemini - OpenAI - OpenAI Compatible - Open Router - xAI (Grok) ## Configure your LLM ### Create a Datacoves Secret Creating a [Datacoves Secret](/docs/how-tos/datacoves/how_to_secrets) requires some key fields to be filled out: - **Name:** The secret must be named `datacoves-copilot-api-configs` - **Description:** Provide a simple description such as: `Datacoves Copilot config` - **Format:** Select `Raw JSON` - **Value**: The value will vary depending on the LLM you are utilizing, see the provider tabs below. - **Scope:** Select the desired scope, either `Project` or `Environment`. - **Project/Environment:** Select the `Project` or `Environment` that will access this LLM. Lastly, be sure to toggle on the `Share with developers` option so that users with developer access will be able to use the LLM. ![Share with Devs](assets/llm_share_with_devs.png) ### Example Secret ![Datacoves Secret creation form for datacoves-copilot-api-configs with Raw JSON format, project scope, and Share with developers enabled](assets/v2_llm_example.png) ### Generative AI Use Cases with Datacoves Copilot #### Expand SELECT \* into columns #### Generate Airflow DAG to run DBT ## LLM Providers Select your provider to see the secret value format and supported models: ### Anthropic Anthropic is an AI safety and research company that builds reliable, interpretable, and steerable AI systems. Their Claude models are known for their strong reasoning abilities, helpfulness, and honesty. Website: https://www.anthropic.com/ #### Secret value format ```json { "default": { "todoListEnabled": true, "consecutiveMistakeLimit": 3, "apiKey": "", "apiModelId": "", "apiProvider": "anthropic", "id": "default" } } ``` #### Getting an API Key 1. Sign Up/Sign In: Go to the Anthropic Console. Create an account or sign in. 2. Navigate to API Keys: Go to the API keys section. 3. Create a Key: Click "Create Key". Give your key a descriptive name (e.g., "Datacoves"). 4. Copy the Key: Important: Copy the API key immediately. You will not be able to see it again. Store it securely. #### Supported Models (`apiModelId`) Datacoves Copilot supports the following Anthropic Claude models: - claude-sonnet-4-5 (Recommended - default) - claude-sonnet-4-20250514 - claude-opus-4-5-20251101 - claude-opus-4-1-20250805 - claude-opus-4-20250514 - claude-haiku-4-5-20251001 - claude-3-7-sonnet-20250219 - claude-3-7-sonnet-20250219:thinking (Extended Thinking variant) - claude-3-5-sonnet-20241022 - claude-3-5-haiku-20241022 - claude-3-opus-20240229 - claude-3-haiku-20240307 See [Anthropic's Model Documentation](https://docs.claude.com/en/docs/about-claude/models/overview) for more details on each model's capabilities. ### OpenAI Datacoves Copilot supports accessing models directly through the official OpenAI API, including the latest GPT-5 family with advanced features like reasoning effort control and verbosity settings. Website: https://openai.com/ #### Secret value format ```json { "default": { "reasoningEffort": "medium", "apiModelId": "", "openAiNativeApiKey": "", "apiProvider": "openai-native", "id": "default" } } ``` #### Getting an API Key 1. Sign Up/Sign In: Go to the OpenAI Platform. Create an account or sign in. 2. Navigate to API Keys: Go to the API keys page. 3. Create a Key: Click "Create new secret key". Give your key a descriptive name (e.g., "Datacoves"). 4. Copy the Key: Important: Copy the API key immediately. You will not be able to see it again. Store it securely. #### Supported Models (`apiModelId`) ##### GPT-5.x Family (Latest) The GPT-5.x models are OpenAI's most advanced, offering superior coding capabilities and agentic task performance: - gpt-5.1-codex-max (default) - Most intelligent coding model optimized for long-horizon, agentic coding tasks (400K context) - gpt-5.2 - Flagship model for coding and agentic tasks across industries (400K context) - gpt-5.2-chat-latest - Optimized for conversational AI and chat use cases - gpt-5.1 - Best model for coding and agentic tasks across domains (400K context) - gpt-5.1-codex - Optimized for agentic coding in Codex (400K context) - gpt-5.1-codex-mini - Cost-efficient version optimized for agentic coding (400K context) ##### GPT-5 Family - gpt-5 - Best model for coding and agentic tasks across domains (400K context) - gpt-5-mini - Faster, cost-efficient for well-defined tasks - gpt-5-nano - Fastest, most cost-efficient option - gpt-5-codex - Specialized coding model ##### GPT-4.1 Family Advanced multimodal models with balanced capabilities: - gpt-4.1 - Advanced multimodal model - gpt-4.1-mini - Balanced performance - gpt-4.1-nano - Lightweight option ##### o3 Reasoning Models Models with configurable reasoning effort for complex problem-solving: - o3, o3-high, o3-low - Different reasoning effort presets - o3-mini (medium reasoning effort) - o3-mini-high (high reasoning effort) - o3-mini-low (low reasoning effort) ##### o4 Models Latest mini reasoning models: - o4-mini - o4-mini-high - o4-mini-low ##### o1 Family Original reasoning models: - o1 - Original reasoning model - o1-preview - Preview version - o1-mini - Smaller variant ##### GPT-4o Family Optimized GPT-4 models: - gpt-4.5-preview - gpt-4o - Optimized GPT-4 - gpt-4o-mini - Smaller optimized variant Refer to the [OpenAI Models documentation](https://platform.openai.com/docs/models) for the most up-to-date list of models and capabilities. ### Azure OpenAI Datacoves Copilot supports Azure OpenAI models through the OpenAI API compatible interface. Website: https://azure.microsoft.com/en-us/products/ai-services/openai-service #### Secret value format ```json { "default": { "apiProvider": "openai", "openAiApiKey": "", "openAiBaseUrl": "https://.cognitiveservices.azure.com/openai/deployments//chat/completions?api-version=", "openAiModelId": "", "openAiUseAzure": true, "id": "default" } } ``` #### Getting Azure OpenAI Credentials 1. **Create Azure OpenAI Resource**: Go to Azure Portal and create an Azure OpenAI service resource 2. **Deploy a Model**: In Azure AI Foundry, deploy a model (e.g., gpt-4o, gpt-4.1, gpt-5) 3. **Get Endpoint**: Copy your endpoint URL from the resource overview 4. **Get API Key**: Navigate to "Keys and Endpoint" section and copy one of the API keys 5. **Get Deployment Name**: Use the deployment name you created (not the model name) #### Endpoint URL Format Datacoves Copilot uses the **Chat Completions API**, so your `openAiBaseUrl` must use the chat completions path, including the deployment name and `api-version` parameter: ``` https://.cognitiveservices.azure.com/openai/deployments//chat/completions?api-version= ``` For models that support **both** the Responses API and the Chat Completions API (for example GPT-4.1, GPT-5.x, o3, o4-mini), **always** use the Chat Completions URL above in Datacoves Copilot and **do not** use the default `/openai/v1/responses` URL shown in some Azure examples. #### Supported Azure OpenAI Models (`openAiModelId`) Use your **deployment name** from Azure AI Foundry as the `openAiModelId`. This must match the deployment name exactly (for example `gpt-5.1` if your deployment is named `gpt-5.1`), not just the base model family name. ##### GPT-5 Series (Latest) **Models with Chat Completions API support:** - gpt-5.2 (2025-12-11) - Flagship model, 400K context - gpt-5.2-chat (2025-12-11) - Chat optimized - gpt-5.1 (2025-11-13) - Advanced reasoning, 400K context - gpt-5.1-chat (2025-11-13) - Chat optimized reasoning - gpt-5 (2025-08-07) - Advanced reasoning, 400K context - gpt-5-mini (2025-08-07) - Cost-efficient, 400K context - gpt-5-nano (2025-08-07) - Fast, cost-efficient, 400K context - gpt-5-chat (2025-08-07, 2025-10-03) - Conversational, 128K context - gpt-oss-120b - Open-weight reasoning model - gpt-oss-20b - Open-weight reasoning model **Note:** The following GPT-5 models use Responses API only and are **not supported** by Datacoves Copilot: - gpt-5-codex, gpt-5-pro, gpt-5.1-codex, gpt-5.1-codex-mini, gpt-5.1-codex-max ##### GPT-4.1 Series - gpt-4.1 (2025-04-14) - Advanced multimodal, 1M context - gpt-4.1-mini (2025-04-14) - Balanced performance, 1M context - gpt-4.1-nano (2025-04-14) - Lightweight, 1M context ##### GPT-4o Series - gpt-4o (2024-11-20) - Optimized GPT-4, 128K context - gpt-4o (2024-08-06) - Optimized GPT-4, 128K context - gpt-4o (2024-05-13) - Original GPT-4o, 128K context - gpt-4o-mini (2024-07-18) - Fast, cost-efficient, 128K context ##### GPT-4 Series - gpt-4 (turbo-2024-04-09) - GPT-4 Turbo with Vision, 128K context ##### o-Series Reasoning Models - o3 (2025-04-16) - Reasoning model, 200K context - o4-mini (2025-04-16) - Mini reasoning, 200K context - o3-mini (2025-01-31) - Compact reasoning, 200K context - o1 (2024-12-17) - Reasoning model, 200K context - o1-mini (2024-09-12) - Smaller reasoning, 128K context - codex-mini (2025-05-16) - Coding specialized, 200K context ##### GPT-3.5 Series - gpt-35-turbo (0125) - Chat optimized, 16K context - gpt-35-turbo (1106) - Chat optimized, 16K context - gpt-35-turbo-instruct (0914) - Completions API only **Important Notes:** - Datacoves Copilot uses the Chat Completions API endpoint - Responses API is not currently supported for Azure OpenAI - Use your **deployment name** in Azure as both the URL path segment and `openAiModelId` - The `api-version` parameter is required in the endpoint URL Refer to [Azure OpenAI documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/concepts/models) for the most current model availability and regional deployment options. ### OpenAI Compatible Datacoves Copilot supports a wide range of AI model providers that offer APIs compatible with the OpenAI API standard. This means you can use models from providers other than OpenAI, while still using a familiar API interface. This includes providers like: - Local models running through tools like Ollama and LM Studio (covered in separate sections). - Cloud providers like Perplexity, Together AI, Anyscale, and others. - Any other provider offering an OpenAI-compatible API endpoint. **Note:** For Azure OpenAI, see the dedicated Azure OpenAI tab for specific setup instructions. #### Secret value format ```json { "default": { "reasoningEffort": "medium", "openAiBaseUrl": "", "openAiApiKey": "", "openAiModelId": "", "openAiUseAzure": false, "azureApiVersion": "", "openAiHeaders": {}, "apiProvider": "openai", "id": "default" } } ``` Where: 1. `openAiBaseUrl`: This is the API endpoint for the provider. It will not be https://api.openai.com/v1 (that's for the official OpenAI API). 2. `openAiApiKey`: This is the secret key you obtain from the provider. 3. `openAiModelId`: This is the model name of the specific model, each provider will expose a different set of models, please check provider's documentation. ##### Fine tune model usage Fine tune model usage using this additional configuration under the `openAiCustomModelInfo` key. ```json "openAiCustomModelInfo": { "maxTokens": -1, "contextWindow": 128000, "supportsImages": true, "supportsPromptCache": false, "inputPrice": 0, "outputPrice": 0, "reasoningEffort": "medium" } ``` ### Google Gemini Datacoves Copilot supports Google's Gemini family of models through the Google AI Gemini API. Website: https://ai.google.dev/ #### Secret value format ```json { "default": { "apiModelId": "", "geminiApiKey": "", "apiProvider": "gemini", "id": "default" } } ``` #### Getting an API Key 1. Go to Google AI Studio: Navigate to https://ai.google.dev/. 2. Sign In: Sign in with your Google account. 3. Create API Key: Click on "Create API key" in the left-hand menu. 4. Copy API Key: Copy the generated API key. #### Supported Models (`apiModelId`) Datacoves Copilot supports the following Gemini models: ##### Gemini 3 (Latest) - gemini-3-pro-preview (Recommended - default) - 1M token context window with reasoning support - gemini-3-flash-preview - Fast, cost-efficient with 1M token context window ##### Gemini 2.5 Pro Models - gemini-2.5-pro - 1M token context with thinking support - gemini-2.5-pro-preview-06-05 - gemini-2.5-pro-preview-05-06 - gemini-2.5-pro-preview-03-25 ##### Gemini 2.5 Flash Models - gemini-flash-latest - Always uses the newest stable Flash model - gemini-2.5-flash - 1M token context with thinking support - gemini-2.5-flash-preview-09-2025 - gemini-flash-lite-latest - Lightweight option - gemini-2.5-flash-lite-preview-09-2025 Refer to the [Gemini documentation](https://ai.google.dev/gemini-api/docs/models) for more details on each model. ### DeepSeek Datacoves Copilot supports accessing models through the DeepSeek API, including deepseek-chat and deepseek-reasoner. Website: https://platform.deepseek.com/ #### Secret value format ```json { "default": { "apiModelId": "", "deepSeekApiKey": "", "apiProvider": "deepseek", "id": "default" } } ``` #### Getting an API Key 1. Sign Up/Sign In: Go to the DeepSeek Platform. Create an account or sign in. 2. Navigate to API Keys: Find your API keys in the API keys section of the platform. 3. Create a Key: Click "Create new API key". Give your key a descriptive name (e.g., "Datacoves"). 4. Copy the Key: Important: Copy the API key immediately. You will not be able to see it again. Store it securely. #### Supported Models (`apiModelId`) - deepseek-chat (Recommended for coding tasks) - deepseek-reasoner (Recommended for reasoning tasks) - deepseek-r1 ### Open Router OpenRouter is an AI platform that provides access to a wide variety of language models from different providers, all through a single API. This can simplify setup and allow you to easily experiment with different models. Website: https://openrouter.ai/ #### Secret value format ```json { "default": { "reasoningEffort": "medium", "openRouterApiKey": "", "openRouterModelId": "", "apiProvider": "openrouter", "id": "default" } } ``` #### Getting an API Key 1. Sign Up/Sign In: Go to the OpenRouter website. Sign in with your Google or GitHub account. 2. Get an API Key: Go to the keys page. You should see an API key listed. If not, create a new key. 3. Copy the Key: Copy the API key. #### Supported Models (`openRouterModelId`) OpenRouter supports a large and growing number of models. Refer to the [OpenRouter Models page](https://openrouter.ai/models) for the complete and up-to-date list. ### xAI (Grok) xAI is the company behind Grok, a large language model known for its conversational abilities and large context window. Grok models are designed to provide helpful, informative, and contextually relevant responses. Website: https://x.ai/ #### Secret value format ```json { "default": { "reasoningEffort": "medium", "apiModelId": "", "xaiApiKey": "", "apiProvider": "xai", "id": "default" } } ``` #### Getting an API Key 1. Sign Up/Sign In: Go to the xAI Console. Create an account or sign in. 2. Navigate to API Keys: Go to the API keys section in your dashboard. 3. Create a Key: Click to create a new API key. Give your key a descriptive name (e.g., "Datacoves"). 4. Copy the Key: Important: Copy the API key immediately. You will not be able to see it again. Store it securely. #### Supported Models (`apiModelId`) - grok-code-fast-1 (Default) - xAI's Grok Code Fast model with 262K context window and prompt caching, optimized for reasoning and coding tasks - grok-4 - xAI's Grok-4 model with 262K context window, image support, and prompt caching - grok-3 - xAI's Grok-3 model with 128K context window and prompt caching - grok-3-fast - xAI's Grok-3 fast model with 128K context window and prompt caching - grok-3-mini - xAI's Grok-3 mini model with 128K context window, reasoning support, and prompt caching - grok-3-mini-fast - xAI's Grok-3 mini fast model with 128K context window, reasoning support, and prompt caching - grok-2-1212 - xAI's Grok-2 model (version 1212) with 128K context window - grok-2-vision-1212 - xAI's Grok-2 Vision model (version 1212) with image support and 32K context window Learn more about available models at [xAI Docs](https://docs.x.ai/docs/models). --- // File: how-tos/vs-code/datacoves-copilot/README # AI LLMs for Datacoves Copilot Datacoves can integrate seamlessly with your existing ChatGPT or Azure OpenAI LLMs. These how tos will go over configuration and usage of AI within Datacoves. ## Prereqs - Have an existing LLM such as ChatGPT or [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/assistants-quickstart?tabs=command-line%2Ckeyless%2Ctypescript-keyless&pivots=ai-foundry-portal) - Have access to API and Endpoint url credentials. - Have `Admin` access to configure credentials in Datacoves ## How to's - [Datacoves Copilot](/docs/how-tos/vs-code/datacoves-copilot/copilot) --- // File: how-tos/vs-code/external-ai-tools/openai-codex # OpenAI Codex [OpenAI Codex](https://openai.com/codex/) is an AI coding assistant available as both a CLI tool and a VS Code extension in Datacoves. ## Prerequisites - A [ChatGPT](https://chatgpt.com) account - Codex enabled in your Datacoves environment. Contact [Datacoves support](mailto:support@datacoves.com) to enable it. ## Enable device code login Before logging in from Datacoves, enable device code login in your ChatGPT account. **Personal accounts:** go to your [ChatGPT security settings](https://chatgpt.com/security-settings) and enable device code login. **Workspace / Enterprise accounts:** a workspace admin must enable device code authentication in the [admin permissions](https://chatgpt.com/admin/permissions) page. For more details, see [OpenAI's authentication docs](https://developers.openai.com/codex/auth). ## Login 1. Open a terminal in your Datacoves VS Code workspace 2. Run: ```bash codex login --device-auth ``` 3. The CLI will display a URL and a one-time code 4. Open the URL in any browser, enter the code, and sign in with your ChatGPT account Both the CLI and the VS Code extension are now authenticated. :::note You may need to reload your browser tab after CLI login for the VS Code extension to pick up the credentials. ::: ## Learn more - [OpenAI Codex documentation](https://developers.openai.com/codex) - [Codex authentication](https://developers.openai.com/codex/auth) - [Codex CLI reference](https://developers.openai.com/codex/cli/reference) --- // File: how-tos/vs-code/external-ai-tools/claude-code # Claude Code [Claude Code](https://www.anthropic.com/claude-code) is an AI coding assistant available as both a CLI tool and a VS Code extension in Datacoves. ## Prerequisites - A [Claude](https://claude.ai) account - Claude Code enabled in your Datacoves environment. Contact [Datacoves support](mailto:support@datacoves.com) to enable it. ## Login 1. Open a terminal in your Datacoves VS Code workspace 2. Run: ```bash claude login ``` 3. Follow the instructions in the terminal 4. When you get the prompt **"Do you want code-server to open the external website?"**, click **Cancel** 5. Ctrl-click (Cmd-click on Mac) the link shown in the terminal 6. Click open in the promp that appears 7. Authenticate in the browser tab that opens 8. Copy the code shown after authentication and paste it back in the terminal where prompted 9. Press **Enter** Both the CLI and the VS Code extension are now authenticated. ## Using the extension By default the Claude Code chat opens in the right sidebar. That is where you type. The activity bar on the far left also has a Claude Code icon, but it opens the sessions list (new session, history), not the chat. Both come from the same extension. If you move the chat into the left sidebar, a second Claude Code icon shows up there for it. ### Open the chat - Click the Claude Code icon in the editor toolbar (top-right of the editor). It shows when a file is open. - Or open the Command Palette (`Ctrl+Shift+P`) and run **Claude Code: Focus on Claude Code View**. ### Move the panel Drag the panel's tab or title bar to where you want it: the right sidebar, the left sidebar, or the editor area. Claude remembers the location. ### File context is automatic The file you have open and any text you select are added to your prompt automatically. The prompt box shows the active file as a chip, and a "N lines selected" note when you select text. Press `Alt+K` to insert an @-mention with the file and line numbers. ## Trouble copying text from the terminal If you have problems copying text from Claude Code in the VS Code terminal, run this command inside Claude Code: ``` /tui default ``` ## Learn more - [Claude Code VS Code extension documentation](https://code.claude.com/docs/en/vs-code) --- // File: reference/troubleshooting # Troubleshooting errors If you hit an error page in Datacoves, it shows an HTTP status code (for example `403`, `500`, or `503`). Most errors clear up on their own with a couple of quick steps. ## First steps 1. **Log out and log back in.** Many errors are caused by an expired session. Use the user menu to log out, then sign in again. 2. **Wait a moment and retry.** If your environment was just started, it may still be coming online. Give it a minute and reload. If the error keeps happening after these steps, contact us at [support@datacoves.com](mailto:support@datacoves.com) and include the status code shown on the error page. ## What the codes mean ### 400 - Bad Request {#400} The request could not be completed. Log out and back in, then try again. ### 401 - Unauthorized {#401} Your session is not valid. Log out and back in to sign in again. ### 403 - Forbidden {#403} You are signed in but do not have permission for that action. Ask your account administrator if you think you should have access. ### 404 - Not Found {#404} The page or resource could not be found. Make sure the link is correct; if you reached it from inside Datacoves, log out and back in and try again. ### 408 - Request Timeout {#408} The request took too long. Wait a moment and retry. ### 409 - Conflict {#409} The request conflicts with the current state (for example something already exists). Refresh and try again. ### 422 - Unprocessable Entity {#422} The request could not be processed. Check your input and try again. ### 429 - Too Many Requests {#429} You have been rate limited. Wait a moment and retry. ### 500 - Internal Server Error {#500} An unexpected error on our side. If it keeps happening, contact support and include the code shown on the error page. ### 502 - Bad Gateway {#502} Datacoves is temporarily unavailable or your environment is still starting. Wait a moment and retry. ### 503 - Service Unavailable {#503} Datacoves is temporarily unavailable or your environment is still starting. Wait a moment and retry. ### 504 - Gateway Timeout {#504} Datacoves took too long to respond. Wait a moment and retry. ### Connection problem {#connection} We could not reach Datacoves. Check your internet connection, then log out and back in. --- // File: reference/admin-menu/README # Admininstration Menu The Datacoves Administration menu provides access to configurations of projects, environments, users, and connections. The following pages explain each area in more detail. ![Account Administration](./assets/menu_admin.gif) --- // File: reference/admin-menu/settings_billing # Account Settings & Billing ## Overview This page is where you adjust all your account level settings such as the account owner, your subscription type and billing options. Here you can also delete your Datacoves account. ## Account Settings This page is divided into three main sections: 1. `Account Settings`, where you can change your account `name` which is displayed to the right of the Datacoves logo. ![Settings and Billing Settings](./assets/settingsbilling_landing_settings.png) 2. `Account Subscription` is where you manage your subscription plan and billing period. managing invoices and payments. The `Manage Subscription` button will take you to Stripe where you can see invoices and payments. ![Settings and Billing Subscription](./assets/settingsbilling_landing_subscription.png) 3. The `Danger Zone` section is where you can delete your account and associated data. ![Settings and Billing Danger](./assets/settingsbilling_landing_danger.png) --- // File: reference/admin-menu/connection_templates # Connection Templates Admin ## Overview A Connection Template in Datacoves defines the basic information for your data warehouse. It acts as a template that can then be used for user and service connections. This simplifies the onboarding process for users. >[!TIP]See our How To - [Connection Templates](/docs/how-tos/datacoves/how_to_connection_template) ## Connection Templates Listing ![Connections Listing](./assets/connections_landing.png) On the Connection Templates landing page you can see a list of Connection Templates associated with each of your Datacoves projects. For each template we can see the provider (i.e. Snowflake, Redshift) and the number of service and user accounts associated with each template. Each row contains 2 action buttons, Edit and Delete. --- // File: reference/admin-menu/environments # Environments Admin ## Overview An Environment in Datacoves defines a data stack and associated settings for a given project. These data stacks are isolated from each other and can be created for long term or temporary use to perform some tests such as to try out a new version of dbt with your project. These environments are displayed on the launchpad to users that have the proper permission for the given environment. :::tip See our How To - [Environments](/docs/how-tos/datacoves/how_to_environments) ::: ![Launch Pad](./assets/launchpad_environments_projects.png) ## Environment Listing ![Environments Listing](./assets/environments_landing.png) On the Environments landing page you can see a list of environments associated with each of your Datacoves projects. For each environment we can see the associated project, the name of the environment to be displayed on the landing page, and the number of associated service connections. Each row contains 2 action buttons, Edit and Delete. --- // File: reference/admin-menu/external_links # External Links ## Overview External links are a kind of bookmark which can be added by an administrator to the standard tabs atop a Datacoves screen: Docs, Load, Transform, Observe, Orchestrate, and Analyze. They are useful for ensuring all users of a given environment have access to commonly used websites. ![Link in use](./assets/extlink_result.png) ## Managing links From the Launchpad sidebar, choose `External Links`: ![External links administration](./assets/extlink_landing.png) You may choose an environment and look at the links already made. - Edit and delete options are next to each link - To add a link, click `+` next to the environment you wish to add a link to When editing or adding a link, a small dialog will appear. You can then add the title as you wish it to appear in the menu, a URL, and the tab it should appear under: ![External links dialog](./assets/extlink_dialog.png) When finished, click Save. --- // File: reference/admin-menu/groups # Groups Admin ## Overview A Group in Datacoves is a collection of permissions, which can be assigned to your account's users. By default, one default group exists for your account, the `Account Admin`. When you create a [Project](/docs/reference/admin-menu/projects), four groups are created: - `Project Admin` - `Project Developer` - `Project Sys Admin` - `Project Viewer` Additionally, when an [Environment](/docs/reference/admin-menu/environments) is created, four additional groups are created for each environment: - `Environment Admin` - `Environment Developer` - `Environment Sys Admin` - `Environment Viewer` :::tip See our How To - [Groups](/docs/how-tos/datacoves/how_to_groups) for information on editing group permissions and associating groups with AD groups for Datacoves enterprise installations. ::: ### **User Groups & Default Privileges in Datacoves** | **Group Type** | **Group Name** | **Default Privileges** | |----------------------------|--------------------------------------------------------|--------------------------------------------------------------------------------------------------------------| | **Admin** | Datacoves Admin | Manages **billing, Grafana, platform-wide settings**, and other administrative tasks such as managing users, creating environments, and service connections. | | **Project Admin** | _\ \_ Project Admin | Has **full control** over project-level settings, including enabling tools. Has access to **Airflow Variables and Connections**. Can create **DataHub integrations**. Can access all tools under each environment in the project. Can manage **Grafana dashboards**. | | **Project Developer** | _\ \_ Project Developer | Can access all environments within the project. Gets an individual **VS Code IDE** for each Development environment. Can create and modify **Superset objects**. Has **editor access in DataHub**. Can **use Airbyte**. Has viewer access in **Grafana**. | | **Project Sys Admin** | _\ \_ Project Sys Admin | Can access all environments within the project. Can access **Superset and DataHub data sources**. Can **see the Airflow Admin menu**, **create Airflow connections**, and **trigger DAGs**, but **cannot access or add Airflow Variables**. Has **editor access in DataHub**. Can **use Airbyte**. Has viewer access in **Grafana**. Can create and modify **Superset objects**. | | **Project Viewer** | _\ \_ Project Viewer | Can access all environments within the project. Can view **dbt docs in all environments**. Has viewer access to **airflow**, **datahub**, **superset** and **grafana**. | | **Environment Admin** | _\ (\)_ Environment Admin | Has **admin rights** for the environment and enabled tools. Has **Airflow Admin rights**, can **extract Airflow variables**, create **DataHub integrations**, and configure **Superset security settings**. Can manage **Grafana** dashboards. | | **Environment Developer** | _\ (\)_ Environment Developer | Can access only the specific environment. Gets an individual **VS Code IDE** for the environment. Can create and modify **Superset objects**. Has **editor access in DataHub**. Can **use Airbyte**. Has viewer access in **Grafana**. | | **Environment Sys Admin** | _\ (\)_ Environment Sys Admin | Can access **Superset and DataHub data sources**. Can **see the Airflow Admin menu**, **create Airflow connections**, and **trigger DAGs**, but **cannot access or add Airflow Variables** (must be added by someone else for security). Has **editor access in DataHub**. Can **use Airbyte**. Has viewer access in **Grafana**. Can create and modify **Superset objects**. | | **Environment Viewer** | _\ \_ Environment Viewer | Can view **dbt docs only in the specific environment**. Has viewer access to **airflow**, **datahub**, **superset** and **grafana**. | --- ### **Tool-Specific Group Requirements** | **Tool** | **Required Roles** | |--------------|-------------------| | **Airbyte** | Must have **Admin, Sys Admin, or Developer** to use Airbyte. | | **Team Airflow** | Must have **Environment Admin or Project Admin** to extract variables. **Sys Admins** can see the **Admin menu** and create connections but **cannot access or add variables**. **Sys Admins & Developers** can trigger DAGs. | | **My Airflow** | Must have **Environment Developer or Project Developer** and **Environment Sysadmin or Project Sysadmin** to access My Airflow. | | **DataHub** | Must have **Environment Admin or Project Admin** to create integrations. **Developers and Sys Admins** have **editor access in DataHub**. | | **dbt Docs** | Must have **Production Environment Developer or Viewer** to view **dbt docs** in production. **Developers** can run **local dbt-docs**. | | **Superset** | Must have **Environment Admin or Project Admin** to modify security settings. Developers can create and modify **Superset objects**. | ## Groups Listing ![Groups Listing](./assets/groups_listing.gif) On the Groups landing page you can see your account's list of groups For each group we can see the group's name, the number of permissions it has, and how many users are assigned to it. Each row contains 2 action buttons, Edit and Delete. --- // File: reference/admin-menu/integrations # Integrations Admin ## Overview Integrations are used to configure external services such as Email, MS Teams and Slack notifications For more information see: - SMTP: used to [send email notifications from Airflow](/docs/how-tos/airflow/send-emails) - MS Teams: used to [send Microsoft Teams messages from Airflow](/docs/how-tos/airflow/send-ms-teams-notifications) - Slack: used to [send Slack messages from Airflow](/docs/how-tos/airflow/send-slack-notifications) :::tip See our How To - [Integrations](/docs/how-tos/datacoves/how_to_integrations) ::: ## Integrations Listing ![Integrations Listing](./assets/integration_landing.png) On the Integrations landing page you can see a list of integrations defined for your Datacoves account. For each integration we can see the name of the integration and the integration type. Each row contains 2 action buttons, Edit and Delete. --- // File: reference/admin-menu/invitations # Invitations Admin ## Overview This page is used to invite users into your account. :::tip See our How To - [Invitations](/docs/how-tos/datacoves/how_to_invitations) ::: ## Invitation Listing This grid shows all pending invitations for your account. Each row also has two action buttons `delete` which cancels an invitation and `resend` to resend an invitation link. ![Invitation Landing](./assets/invitations_landing.png) --- // File: reference/admin-menu/projects # Projects Admin ## Overview A Project is the highest grouping in Datacoves. It is what contains environments, which then are linked to services, connections, etc. The Datacoves landing page (Launch Pad) follows this hierarchy: :::tip See our How To - [Projects](/docs/how-tos/datacoves/how_to_projects) ::: ![Project Environment Difference](./assets/launchpad_environments_projects.png) ## Projects Listing ![Projects Listing](./assets/projects_landing.png) On the Projects landing page you can see a list of projects associated with your Datacoves account. For each project, you will see number of defined connection templates and environments. You will also see the status of the git connection(tested or not). Each row contains 3 action buttons, Test, Edit and Delete. ### Testing connection Testing your repo connection ensures that services like dbt docs and Orchestration are available. It is important to test the connection to git to make sure the system can clone your repository. If the test fails, this indicates that Datacoves cannot clone your repository this will affect serving production dbt docs and Airflow jobs. Edit your environment and check your settings then click the test button again to assure the git status is "Tested". --- // File: reference/admin-menu/secrets # Secrets Admin ## Overview Secrets are used to manage confidential information that are used by tools running in VSCode, or services like Airflow. Some uses could be: - Storing Airbyte connections credentials using `dbt-coves extract` and `dbt-coves load` commands. - Storing Airflow connections or variables used by Airflow DAGs :::tip See our How To - [Secrets](/docs/how-tos/datacoves/how_to_secrets) ::: ## Secrets Listing ![Secrets Listing](./assets/secrets_landing.png) On the Secrets landing page you can see a list of secrets defined for your Datacoves account. Each secret belongs to a project, has a name, tags, and an author. ### Sharing secrets When a new secret is created, it can be shared across the entire project, or shared with just one environment. It could also be treated as a personal secret when it's shared with no environment/project. In such case, only the author can retrieve it's value. ### Secrets store Secrets could be stored encrypted on the Datacoves database, or use a third party service, such as Amazon Secrets Manager, among others. --- // File: reference/admin-menu/service_connections # Service Connections Admin ## Overview Service Connections are used by automated processes like Airflow jobs. Before Datacoves 3.3 details entered here could only be injected as **environment variables** that would then be used within a dbt profiles.yml file to establish a connection with your data warehouse. However, it is now recommended to select **Airflow Connection** as the delivery mode so that the credentials are used to create an Airflow connection to establish a connection with your data warehouse. :::tip See our How To - [Service Connections](/docs/how-tos/datacoves/how_to_service_connections) ::: ## Service Connection Listing ![Service Connections Listing](./assets/serviceconnection_landing.png) On the Service Connections landing page you can see a list of service connections associated with each of your environments. For each environment we can see the associated environment, the service that uses the connection, the name of the service connection, the warehouse type, and whether the connection was tested to assure the credentials are valid. Each row contains 3 action buttons: Test Connection, Edit, and Delete. :::tip Clicking the (?) icon will show the names of the ENVIRONMENT variables that will be injected into the service. These are what you must use in your dbt profiles.yml file. ::: ## Datacoves Airflow Variables Datacoves uses the service connection to dynamically create the following variables which are then injected into Airflow. - `DATACOVES____ROLE` - `DATACOVES____ACCOUNT` - `DATACOVES____WAREHOUSE` - `DATACOVES____ROLE` - `DATACOVES____DATABASE` - `DATACOVES____SCHEMA` - `DATACOVES____USER` - `DATACOVES____PASSWORD` --- // File: reference/admin-menu/users # Users Admin ## Overview In this page you can manage the users that belong to your account. Here you can grant or change the permission groups associated with each user. :::tip See our How To - [Manage Users](/docs/how-tos/datacoves/how_to_manage_users) ::: ## Users Listing ![Users Listing](./assets/users_landing.png) On the Users landing page you can see a list of user associated with your Datacoves account. For each user we can see the user's name, their email, the security groups they were granted, and last time they logged into Datacoves. Each row contains 2 action buttons: - Edit - Delete --- // File: reference/airflow/airflow-best-practices # Airflow Best Practices This page should serve as a reference for tips and tricks that we recommend for the best Airflow experience. Please read the official [Airflow Best Practices doc](https://airflow.apache.org/docs/apache-airflow/stable/best-practices.html) first. ## Table of Contents - [Start Date](/docs/reference/airflow/airflow-best-practices#start-date) This page aims to be a reference for airflow recommendations. ### Start Date Do not use [dynamic scheduled dates](https://infinitelambda.com/airflow-start-date-execution-date/). Always set your start date for the day before or sooner and set `catchup=false` to avoid running additional runs: ```python from pendulum import datetime from airflow.decorators import dag @dag( default_args=("start_date": datetime(2023, 12, 29), # Set this to the day before or earlier "owner": "Noel Gomez", "email": "gomezn@example.com", "email_on_failure": True, ), dag_id="sample_dag", schedule="@daily", catchup=False, # Set this to false to avoid additional catchup runs tags=["version_1"], description="Datacoves Sample dag", ) ... ``` --- // File: reference/airflow/airflow-config-defaults # Airflow Config Defaults For security reasons, we do not expose the Airflow config to end users via the `Airflow *Admin -> Configuration` menu option. Below are some of the configs that we use which you may find useful: ``` [celery] worker_concurrency = 16 worker_prefetch_multiplier = 1 operation_timeout = 1.0 task_adoption_timeout = 600 stalled_task_timeout = 0 task_publish_max_retries = 3 [celery_kubernetes_executor] kubernetes_queue = kubernetes ``` ``` [core] executor = KubernetesExecutor default_timezone = utc parallelism = 32 max_active_tasks_per_dag = 16 dags_are_paused_at_creation = True max_active_runs_per_dag = 16 dagbag_import_timeout = 300 dag_file_processor_timeout = 180 task_runner = StandardTaskRunner killed_task_cleanup_time = 60 default_task_retries = 2 ``` ``` [database] max_db_retries = 3 ``` ``` [email] default_email_on_retry = True default_email_on_failure = True ``` ``` [kubernetes] worker_pods_pending_timeout = 600 worker_pods_pending_timeout_check_interval = 120 worker_pods_queued_check_interval = 60 worker_pods_pending_timeout_batch_size = 100 [kubernetes_environment_variables] AIRFLOW__CORE__DEFAULT_TASK_RETRIES = 2 AIRFLOW__CORE__DAGBAG_IMPORT_TIMEOUT = 300 AIRFLOW__SCHEDULER__MIN_FILE_PROCESS_INTERVAL = 180 AIRFLOW__CORE__DAG_FILE_PROCESSOR_TIMEOUT = 180 AIRFLOW__KUBERNETES__WORKER_PODS_PENDING_TIMEOUT = 600 AIRFLOW__SCHEDULER__PARSING_PROCESSES = 1 AIRFLOW__SCHEDULER__MAX_DAGRUNS_PER_LOOP_TO_SCHEDULE = 20 ``` ``` [scheduler] run_duration = 41460 job_heartbeat_sec = 5 scheduler_heartbeat_sec = 5 num_runs = -1 scheduler_idle_sleep_time = 1 min_file_process_interval = 180 deactivate_stale_dags_interval = 60 dag_dir_list_interval = 300 print_stats_interval = 30 pool_metrics_interval = 5.0 scheduler_health_check_threshold = 30 orphaned_tasks_check_interval = 300.0 scheduler_zombie_task_threshold = 300 zombie_detection_interval = 10.0 parsing_processes = 1 trigger_timeout_check_interval = 15 run_duration = 41460 ``` ``` [sensors] default_timeout = 604800 ``` ``` [smtp] smtp_timeout = 30 smtp_retry_limit = 5 ``` ``` [webserver] default_ui_timezone = utc worker_refresh_interval = 6000 log_fetch_timeout_sec = 5 log_fetch_delay_sec = 2 log_auto_tailing_offset = 30 default_dag_run_display_number = 25 auto_refresh_interval = 3 ``` ## Airflow Environment Variables Datacoves injects environment variables into Airflow. **DATACOVES__DAGS_FOLDER**: The folder where Airflow will look for DAGs. This is defined when you set your `python DAGs path` in the [environment setup](/docs/how-tos/datacoves/how_to_environments#services-configuration). **DATACOVES__ENVIRONMENT_SLUG**: The slug for your environment. This is randomly generated upon creation of the environment. **The development slug can be seen on the launchpad screen:** `https://dev123.datacoves.localhost` in this case `DATACOVES__ENVIRONMENT_SLUG=dev123` ![Launch Pad](../admin-menu/assets/launchpad_environments_projects.png) :::note If you have any questions, please send us an email at support@datacoves.com ::: --- // File: reference/airflow/airflow-variables Datacoves injects several environment variables into Apache Airflow to streamline workflow configurations. Below is a list of important variables you may encounter: ## Airflow information Variables containing information about Airflow: - `DATACOVES__AIRFLOW_NOTIFICATION_INTEGRATION`: Notification service for Airflow alerts. May be `SLACK` or `TEAMS` - `DATACOVES__AIRFLOW_TYPE`: May be `team_airflow` or `my_airflow`, useful for environment-specific logic like sending email alerts - `DATACOVES__DAGS_FOLDER`: Path where Airflow searches for DAGs, typically `orchestrate/dags` - `DATACOVES__DBT_HOME`: Read-only dbt home directory containing `dbt_project.yml`, typically `transform` - `DATACOVES__DBT_PROFILE`: Current dbt profile, commonly `default` - `DATACOVES__REPO_PATH`: Path to read-write copy of Airflow repo - `DATACOVES__REPO_PATH_RO`: Path to read-only copy of Airflow repo - `DATACOVES__YAML_DAGS_FOLDER`: Path to YAML files used by dbt-coves to generate Python DAGs, typically `orchestrate/dags_yml_definitions` ## Datacoves environment information Variables containing information about the current Datacoves environment: - `DATACOVES__ACCOUNT_ID`: Account ID number - `DATACOVES__ACCOUNT_SLUG`: Account slug - `DATACOVES__ENVIRONMENT_SLUG` Environment slug (e.g. dev123) - `DATACOVES__PROJECT_SLUG`: Project slug (e.g. balboa-analytics-datacoves) ## Version information Variables containing versions: - `DATACOVES__SQLFLUFF_VERSION`: Current version of SQLFLuff - `DATACOVES__VERSION`: Complete version of Datacoves including patch level - `DATACOVES__VERSION_MAJOR_MINOR`: Datacoves version excluding patch level (e.g. 5.0) - `DATACOVES__VERSION__ENV`: Complete version of Datacoves including patch level for this environment - `DATACOVES__VERSION_MAJOR_MINOR__ENV`: Datacoves version excluding patch level for this environment --- // File: reference/airflow/dag-generators # DAG Generators Within `dbt-coves generate airflow-dags`, DAG Generators are responsible of outputting Python code for Airflow Task Groups from other services. We currently provide Airflow and Fivetran ones, with a dbt variant of each. ## AirbyteGenerator and AirbyteDbtGenerator These generators return Airbyte Sync tasks based on Airbyte Connection IDs and dbt sources respectively. ### AirbyteGenerator params: - `host`: Airbyte's service hostname, typically `envSlug-airbyte-airbyte-service-svc` - `port` - `connection_ids`: list of Airbyte connections - `airbyte_conn_id`: ID of Airbyte's Airflow connection ```yaml [...] nodes: run_airbyte: generator: AirbyteGenerator type: task_group host: env123-airbyte-airbyte-server-svc port: 8000 connection_ids: - 1234-5678-9101-2345 - 0987-6543-2109-8765 airbyte_conn_id: airbyte_in_airflow ``` ### AirbyteDbtGenerator: AirbyteDbtGenerator will match your dbt sources against your Airbyte connections, and create a Sync task for each of them. It's behavior is similar to AirbyteGenerator, though Airbyte connections are "discovered" instead of hard-coded. - `host` - `port` - `airbyte_conn_id` - `dbt_project_path`: optional path to dbt project (it's auto-discovered) - `run_dbt_deps`: whether to run `dbt deps` before obtaining sources (defaults to False) - `run_dbt_compile`: whether to run `dbt compile` before obtaining sources (defaults to False) - `dbt_list_args`: args to be passed to `dbt ls` ```yaml [...] nodes: extract_and_load_airbyte: generator: AirbyteDbtGenerator type: task_group host: env123-airbyte-airbyte-server-svc port: 8000 airbyte_conn_id: airbyte_in_airflow dbt_project_path: /config/workspace/transform run_dbt_deps: true run_dbt_compile: true dbt_list_args: "--select tag:daily_run_airbyte" ``` ## FivetranGenerator and FivetranDbtGenerator These generators return Fivetran Sync tasks based on Fivetran Connection IDs and dbt sources respectively. They behave the exact same as Airbyte ones, the only difference being the necessity of Fivetran's [API Key and Secret](https://fivetran.com/docs/rest-api/getting-started) ### FivetranGenerator params: - `api_key`: - `api_secret`: - `connection_ids`: list of Fivetran connections - `fivetran_conn_id`: ID of Fivetran's Airflow connection - `wait_for_completion`: whether to create an extra sensor-task that polls the sync-task for completion ```yaml [...] nodes: run_fivetran: generator: FivetranGenerator type: task_group api_key: my_api_key api_secret: my_api_secret connection_ids: - two_word - fivetran_ids fivetran_conn_id: fivetran_in_airflow wait_for_completion: true ``` ### FivetranDbtGenerator params: - `host` - `port` - `fivetran_conn_id` - `wait_for_completion` - `dbt_project_path` - `run_dbt_deps` - `run_dbt_compile` - `dbt_list_args` ```yaml [...] nodes: extract_and_load_fivetran: generator: FivetranDbtGenerator type: task_group api_key: my_api_key api_secret: my_api_secret fivetran_conn_id: fivetran_in_airflow wait_for_completion: true dbt_project_path: /config/workspace/transform run_dbt_deps: true run_dbt_compile: true dbt_list_args: "--select tag:daily_run_fivetran" ``` --- // File: reference/airflow/datacoves-decorators # Datacoves Airflow Decorators With the introduction of the task flow API in Airflow we have released the Datacoves decorators to make writing DAGs simple! :::note While the Datacoves decorators are recommended, the [Datacoves Operators](/docs/reference/airflow/datacoves-operator) are still supported. ::: ## Decorators ### @task.datacoves_bash This custom decorator is an extension of Airflow's default @task decorator and should be used to run bash commands, pull secrets etc. **The operator does the following:** - Copies the entire Datacoves repo to a temporary directory, to avoid read-only errors when running `bash_command`. - Activates the Datacoves Airflow virtualenv. - Runs the command in the repository root (or a passed `cwd`, relative path from repo root where to run command from). **Params:** - `env`: Pass in a dictionary of variables. eg `"my_var": "{{ var.value.my_var }}"`. Please use `{{ var.value.my_var }}` syntax to avoid parsing every 30 seconds. - `outlets`: Used to connect a task to an object in datahub or update a dataset - `append_env`: Add env vars to existing ones like `DATACOVES__DBT_HOME` ```python def my_bash_dag(): @task.datacoves_bash def echo_hello_world() -> str: return "Hello World!" dag = my_bash_dag() ``` ### @task.datacoves_dbt This custom decorator is an extension of the @task decorator and simplifies running dbt commands within Airflow. **The operator does the following:** - Copies the entire Datacoves repo to a temporary directory, to avoid read-only errors when running `bash_command`. - It always activates the Datacoves Airflow virtualenv. - If `dbt_packages` isn't found, it'll run `dbt deps` before the desired command. - It runs dbt commands inside the dbt Project Root, not the Repository root. **Params:** Datacoves dbt decorator supports all the [Datacoves dbt Operator params](/docs/reference/airflow/datacoves-operator#datacoves-dbt-operator) plus: - `connection_id`: This is the [service connection](/docs/how-tos/datacoves/how_to_service_connections) which is automatically added to airflow if you select `Airflow Connection` as the `Delivery Mode`. **dbt profile generation:** With the `connection_id` mentioned above, we create a temporary dbt profile (it only exists at runtime inside the Airflow DAG's worker). By default, this dbt profile contains the selected Service Credential connection details. The dbt profile `name` is defined either in Project or Environment settings, in their `Profile name` field. This can be overwritten by passing a custom `DATACOVES__DBT_PROFILE` environment variable to the decorator. Users can also customize this dbt profile's connection details and/or target with the following params: - `overrides`: a dictionary with override parameters such as warehouse, role, database, etc. - `target`: the target name this temporary dbt profile will receive. Defaults to `default`. Basic example: ```python def my_dbt_dag(): @task.datacoves_dbt( connection_id="main" ) def dbt_test() -> str: return "dbt debug" dag = my_dbt_dag() ``` Example with overrides: ```python def my_dbt_dag(): @task.datacoves_dbt( connection_id="main", overrides={"warehouse": "my_custom_wh"}, env={"DATACOVES__DBT_PROFILE": "prod"}, target="testing" ) def dbt_test() -> str: return "dbt debug -t testing" # Make sure to pass `-t {target}` if you are using a custom target name. dag = my_dbt_dag() ``` The examples above use the Airflow connection `main` which is added automatically from the Datacoves Service Connection. ![Service Connection](assets/service_connection_main.jpg) #### Uploading and downloading dbt results From Datacoves 3.4 onwards, the `datacoves_dbt` decorator allows users to upload and download dbt execution results and metadata to our `dbt API`. :::note dbt-API is a feature that is not enabled by default. Please contact support for further assistance. ::: This is particularly useful for performing [dbt retries](/docs/how-tos/airflow/dags/retry-dbt-tasks). The new datacoves_dbt parameters are: - `dbt_api_enabled` (Default: `False`): Whether your Environment includes a dbt API instance. - `download_static_artifacts` (Default: `True`): Whether user wants to download dbt static artifact files. - `upload_static_artifacts` (Default: `False`): Whether user wants to upload dbt static files. - `download_additional_files` (Default: `[]`): A list of extra paths the user wants to download. - `upload_additional_files` (Default: `[]`): A list of extra paths the user wants to upload. - `upload_tag` (Default: DAG `run_id`): The tag/label the files will be uploaded with. - `upload_run_results` (Default: `True`): Whether the `run_results.json` dbt file will be uploaded. - `download_run_results` (Default: `False`): Whether the `run_results.json` dbt file will be downloaded. - `upload_sources_json` (Default: `True`): Whether the `sources.json` dbt file will be uploaded. - `download_sources_json` (Default: `False`): Whether the `sources.json` dbt file will be downloaded. :::note **Static Artifacts** The static artifacts are important dbt-generated files that help with dbt's operations: - `target/graph_summary.json`: Contains a summary of the DAG structure of your dbt project. - `target/graph.gpickle`: A serialized Python networkx graph object representing your dbt project's dependency graph. - `target/partial_parse.msgpack`: Used by dbt to speed up subsequent runs by storing parsed information. - `target/semantic_manifest.json`: Contains semantic information about your dbt project. These files are downloaded by default (when `download_static_artifacts=True`) and are tagged as "latest" when uploaded. ::: ### @task.datacoves_airflow_db_sync :::note The following Airflow tables are synced by default: `ab_permission`, `ab_role`, `ab_user`, `dag`, `dag_run`, `dag_tag`, `import_error`, `job`, `task_fail`, `task_instance`. ::: **Params:** - `db_type`: The data warehouse you are using. Currently supports `redshift` or `snowflake`. - `destination_schema`: The destination schema where the Airflow tables will end up. By default, the schema will be named as follows: `airflow-{datacoves environment slug}`, for example `airflow-qwe123`. - `connection_id`: The name of your Airflow [service connection](/docs/how-tos/datacoves/how_to_service_connections) which is automatically added to airflow if you select `Airflow Connection` as the `Delivery Mode`. - `additional_tables`: A list of additional tables you would want to add to the default set. - `tables`: A list of tables to override the default ones from above. Warning: An empty list `[]` will perform a full-database sync. ```python def airflow_data_sync(): @task.datacoves_airflow_db_sync( db_type="snowflake", destination_schema="airflow_dev", connection_id="load_airflow", # additional_tables=["additional_table_1", "additional_table_2"] ) dag = airflow_data_sync() ``` --- // File: reference/airflow/datacoves-commands # Datacoves CLI Commands The `datacoves` bash commands are meant to simplify your workflow. Currently, the datacoves command has the following sub commands: - `my` ## Datacoves My The `my` subcommand executes commands for My Airflow. Currently, the `datacoves my` subcommand has the following subcommands: - `my import` - `my pytest` - `my api-key` ### datacoves my import :::note For security purposes secret values will not be automatically imported. The tool will ask you to enter the secret value. ::: This command will import your variables and connections from Team Airflow to [My Airflow](/docs/how-tos/my_airflow). You only need to complete this once or whenever new variables/connections are added to team airflow. ```bash datacoves my import ``` ### datacoves my pytest :::note My Airflow [must be instantiated](/docs/how-tos/my_airflow/start-my-airflow) for this command to work. ::: This command allows you to run pytest validations straight from the command line. Simply create your python file with your desired tests inside the `orchestrate` directory. Then pass the file path as an argument as seen below. ```bash datacoves my pytest -- orchestrate/test_dags/validate_dags.py ``` ### datacoves my api-key Manage My Airflow API keys from the command line. These keys allow you to access the My Airflow API programmatically. #### List existing keys ```bash datacoves my api-key list ``` This displays all environments where My Airflow is enabled, along with the API URL and any existing keys. #### Generate a new key ```bash datacoves my api-key generate ``` You can optionally provide a name for the key: ```bash datacoves my api-key generate --name "My Script" ``` The command will output the API URL and key. Save the key immediately as it won't be shown again. #### Delete a key ```bash datacoves my api-key delete ``` Use the first 8 characters of the token (shown in the list command) to identify which key to delete. ```bash datacoves my api-key delete abc12345 ``` --- // File: reference/airflow/environment-service-connection-vars # Warehouse Environment Variables When creating a service connection and setting the `Delivery Mode` to environment variables, Datacoves will inject the following environment variables in Airflow. These variables can be used in your `profiles.yml` file and will allow you to safely commit a profiles.yml without sensitive data in git. The available environment variables will vary based on your data warehouse. :::note These variables will also need to be configured in your CI/CD provider. ie) github, Gitlab. ::: The name of the service connection will be used to dynamically create the following variables. In the chart below the name of the service connection is `main`. ## Snowflake Environment Variables | Variables | |----------------------------------| | `DATACOVES__MAIN__ACCOUNT` | | `DATACOVES__MAIN__DATABASE` | | `DATACOVES__MAIN__SCHEMA` | | `DATACOVES__MAIN__USER` | | `DATACOVES__MAIN__PASSWORD` | | `DATACOVES__MAIN__ROLE` | | `DATACOVES__MAIN__WAREHOUSE` | ## Redshift Environment Variables | Variables | |----------------------------------| | `DATACOVES__MAIN__HOST` | | `DATACOVES__MAIN__USER` | | `DATACOVES__MAIN__PASSWORD` | | `DATACOVES__MAIN__DATABASE` | ## Big Query Environment Variables | Variables | |----------------------------------| | `DATACOVES__MAIN__DATASET` | | `DATACOVES__MAIN__KEYFILE_JSON` | ## Databricks Environment Variables | Variables | |----------------------------------| | `DATACOVES__MAIN__HOST` | | `DATACOVES__MAIN__SCHEMA` | | `DATACOVES__MAIN__HTTP_PATH` | | `DATACOVES__MAIN__TOKEN` | | `DATACOVES__MAIN__TYPE` | --- // File: reference/airflow/datacoves-operator # Datacoves Operators & Generators :::note All operators use Datacoves Service connections with `Delivery Mode` set to `Environment Variables`. When utilizing dbt-coves to generate DAGs, it's crucial to grasp the functionality of the two frequently used operators and their behind-the-scenes operations, enhancing your Airflow experience. ::: ## Datacoves Bash Operator ``` from operators.datacoves.bash import DatacovesBashOperator ``` This custom operator is an extension of Airflow's default Bash Operator. It: - Copies the entire Datacoves repo to a temporary directory, to avoid read-only errors when running `bash_command` - Activates the Datacoves Airflow virtualenv - Runs the command in the repository root (or a passed `cwd`, relative path from repo root where to run command from) Params: - `bash_command`: command to run - `cwd` (optional): relative path from repo root where to run command from - `activate_venv` (optional): whether to activate the Datacoves Airflow virtualenv or not ```python """## Simple Datacoves DAG This DAG executes a Python script using DatacovesBashOperator. """ from airflow.decorators import dag from operators.datacoves.bash import DatacovesBashOperator from pendulum import datetime @dag( doc_md=__doc__, default_args={ "start_date": datetime(2022, 10, 10), "owner": "Noel Gomez", "email": "gomezn@example.com", "email_on_failure": True, "retries": 3, }, catchup=False, tags=["python_script"], description="Simple Datacoves DAG", schedule="0 0 1 */12 *", ) def simple_datacoves_dag(): run_python_script = DatacovesBashOperator( task_id="run_python_script", bash_command="python orchestrate/python_scripts/sample_script.py", ) simple_datacoves_dag() ``` ## Datacoves dbt Operator :::warning If you have either `dbt_modules` or `dbt_packages` folders in your project repo Datacoves won't run `dbt deps`. ::: ``` from operators.datacoves.dbt import DatacovesDbtOperator ``` This custom operator is an extension of Datacoves Bash Operator and simplifies running dbt commands within Airflow. The operator does the following: - Copies the entire Datacoves repo to a temporary directory, to avoid read-only errors when running `bash_command`. - It always activates the Datacoves Airflow virtualenv. - If 'dbt_packages' isn't found, it'll run `dbt deps` before the desired command - It runs dbt commands inside the dbt Project Root, not the Repository root. Params: - `bash_command`: command to run - `project_dir` (optional): relative path from repo root to a specific dbt project. - `run_dbt_deps` (optional): boolean to force dbt deps run. ```python import datetime from airflow.decorators import dag from operators.datacoves.dbt import DatacovesDbtOperator @dag( default_args={ "start_date": datetime.datetime(2023, 1, 1, 0, 0), "owner": "Noel Gomez", "email": "gomezn@example.com", "email_on_failure": True, }, description="Sample DAG for dbt build", schedule_interval="0 0 1 */12 *", tags=["version_2"], catchup=False, ) def yaml_dbt_dag(): run_dbt = DatacovesDbtOperator( task_id="run_dbt", bash_command="dbt run -s personal_loans" ) yaml_dbt_dag() ``` ## Data Sync Operators To synchronize the Airflow database, we can use an Airflow DAG with one of the Airflow operators below. Datacoves has the following Airflow Data Sync Operators: `DatacovesDataSyncOperatorSnowflake` and `DatacovesDataSyncOperatorRedshift`. Both of them receive the same arguments, so we won't differentiate examples. Select the appropriate provider for your Data Warehouse. :::note To avoid synchronizing unnecessary Airflow tables, the following Airflow tables are synced by default: `ab_permission`, `ab_role`, `ab_user`, `dag`, `dag_run`, `dag_tag`, `import_error`, `job`, `task_fail`, `task_instance` ::: These operators can receive: - `tables`: a list of tables to override the default ones. _Warning:_ An empty list `[]` will perform a full-database sync. - `additional_tables`: a list of additional tables you would want to add to the default set. - `destination_schema`: the destination schema where the Airflow tables will end-up. By default, the schema will be named as follows: `airflow-{datacoves environment slug}` for example `airflow-qwe123` - `service_connection_name` The name of your environment variables from your [service connection](/docs/how-tos/datacoves/how_to_service_connections) which are automatically injected to airflow if you select `Environment Variables` as the `Delivery Mode`. ```python """## Datacoves Airflow db Sync Sample DAG This DAG is a sample using the DatacovesDataSyncOperatorSnowflake Airflow Operator to sync the Airflow Database to a target db """ from airflow.decorators import dag from operators.datacoves.data_sync import DatacovesDataSyncOperatorSnowflake @dag( default_args={"start_date": "2021-01"}, description="sync_data_script", schedule_interval="0 0 1 */12 *", tags=["version_3"], catchup=False, ) def sync_airflow_db(): # service connection name default is 'airflow_db_load'. # Destination type default is 'snowflake' (and the only one supported for now) sync_data_script = DatacovesDataSyncOperatorSnowflake( service_connection_name="airflow_db_load", # this can be omitted or changed to another service connection name. ) sync_airflow_db() ``` --- // File: reference/airflow/airflow-billing # How Datacoves Billing Works This page explains how Datacoves measures and bills Airflow worker usage, what is and isn't included in billed minutes, and how billed usage relates to Airflow's own metadata. ## Overview Datacoves bills Airflow usage based on **worker pod running time**. Each Airflow task runs inside a dedicated Kubernetes pod, and billing is based on how long that pod is alive in the cluster. This is different from the task durations shown in the Airflow UI, which only reflects when task code itself was running. Billing is derived from the Prometheus metric: ``` kube_pod_container_status_running ``` This metric captures the time each worker pod container spends in the `Running` state. The total is summed across all pods in your environment to produce your monthly billed minutes. ## Task execution timeline A typical task goes through eight stages from the moment it is enqueued to the moment its pod is decommissioned. Only some of these stages are billed. | # | Stage | Description | Billed | |---|---|---|---| | 1 | Task Enqueued | `queued_dttm`: task is in the scheduler queue; no pod exists yet. | No | | 2 | Pod Creation | Pod is created in `Pending` state; includes image pulling. | No | | 3 | Init Container | `git-sync` runs to clone or sync the repository. | **Yes** | | 4 | Main Container Start | Pod transitions to `Running` state. | **Yes** | | 5 | Airflow Bootstrap | The worker process initializes and loads the environment. | **Yes** | | 6 | Task Execution | `start_date`: the actual task / DAG code begins running. | **Yes** | | 7 | Task Completion | `end_date`: the task code finishes its execution. | **Yes** | | 8 | Pod Termination | The container stops and the pod is decommissioned. | No | ----------- ![Task Execution Timeline and Billing Logic](./assets/execution-timeline-and-billing-logic.png) Billing starts when the main container reaches `Running` and ends when the pod terminates. Queue time, pod creation, and image pulls are not billed. ## How this maps to Airflow metadata The `task_instance` table in the Airflow metadata database has three timestamp columns relevant here: | Column | Description | |---|---| | `queued_dttm` | When the task was placed in the queue | | `start_date` | When the task code actually started executing | | `end_date` | When the task code finished executing | Comparing those columns to billed time: | Calculation | What it captures | Relation to billing | |---|---|---| | `end_date - queued_dttm` | Queue time + pod init + bootstrap + execution | Overestimates billed minutes | | `end_date - start_date` | Task code execution only | Underestimates billed minutes | | Billed (pod running) | Init container + Main container start + bootstrap + execution + completion | Actual billed value | Neither Airflow column on its own matches the billed value exactly. The billed value sits between the two. ## Querying the Airflow `task_instance` table To estimate usage directly from Airflow metadata: * `queued_dttm` to `end_date` (`queued_plus_execution_time`) overestimates billed minutes. * `start_date` to `end_date` (`execution_time`) underestimates billed minutes. The actual billed figure falls inside that range. Example query: ```sql SELECT date_trunc('day', ti.start_date) AS day, count(*) AS tasks, round(sum(EXTRACT(EPOCH FROM (ti.end_date - ti.queued_dttm)) / 60)::numeric, 1) AS queued_plus_execution_time, round(sum(EXTRACT(EPOCH FROM (ti.end_date - ti.start_date)) / 60)::numeric, 1) AS execution_time, round(sum(EXTRACT(EPOCH FROM (ti.start_date - ti.queued_dttm)) / 60)::numeric, 1) AS queue_time_min FROM task_instance ti WHERE ti.state = 'success' AND ti.start_date >= '2026-03-01 00:00:00+00' AND ti.start_date < '2026-04-01 00:00:00+00' AND ti.queued_dttm IS NOT NULL AND ti.end_date IS NOT NULL GROUP BY 1 ORDER BY 1; ``` --- // File: reference/datacoves/versioning # Datacoves versioning Datacoves uses [semantic versioning](https://semver.org/) in all our docker images, and Datacoves releases. `MAJOR.MINOR.PATCH` The `MAJOR.MINOR` versions are defined as below and `PATCH` is an autogenerated (timestamp) generated when the release is built. ## Our criteria ### When do we bump the `MAJOR` version? When we make incompatible changes or we introduce compatible changes but deprecate features: - Any python library upgrade (including dbt) that requires changes in the customer's analytics(dbt) git repo - Airbyte, Airflow, DataHub, Superset upgrades that require reconfiguration - Datacoves core changes that require human intervention - Airbyte, Airflow, DataHub, Superset that do not require reconfiguration, but several features are being deprecated ### When should we bump the `MINOR` version? - When we make compatible changes, such as new features or upgrade dependencies - Patch version changes to dbt e.g. 1.8.3 to 1.8.5 - Compatible updates to dbt e.g. 1.7.x to 1.8.x - Compatible update to Airbyte, Airflow, DataHub, Superset that do not require reconfiguration ### Everything else is a `PATCH` - Bug fixes, performance enhancements ## Images tags Images are pushed with the folling tags: - MAJOR - MAJOR.MINOR - MAJOR.MINOR.PATCH - MAJOR.MINOR.PATCH-\ CI jobs that use Datacoves images could reference any of the above, depending on how specific the customer needs to be. --- // File: reference/datacoves/vpc-deployment # VPC Deployment Datacoves is designed to work on Public or Private Virtual Clouds. The following diagram shows the main services required by Datacoves when deployed on a VPC. ## Datacoves Architecture ![Datacoves Architecture](./assets/datacoves-architecture.png) ## Dependencies Datacoves can be deployed on AWS, Azure or Google Cloud. Here is the list of services required, each cloud provider offers the service with a different name/brand. | Service | Purpose | Requirements | Quantity | |--------------|-----------------|--------------------------|------------| | Database | Datacoves core services | PostgreSQL > v.14, Minimum 2vcpu, 16Gb memory __(*)__ | 1 server | | Database | Datacoves stack services | PostgreSQL > v.14, Minimum 4vcpu, 32Gb memory __(*)__ | 1 server | | Blob storage | DBT artifacts | N/A | 1 bucket | | Blob storage | Grafana logs | Lifetime policy with 30 days retention | 1 bucket | | Blob storage | Airflow DAGs | N/A | 1 bucket per Airflow instance | | Blob storage | Airbyte logs | N/A | 1 bucket per Airbyte instance | | Blob Storage | Airflow logs | N/A | 1 bucket per Airflow instance | | OIDC provider | Datacoves SSO | Oauth 2.0 OIDC compliant provider | 1 provider (optional) | | Managed Kubernetes | Runs the platform | > v1.34 | Clusters are a minimum of 4 servers, sizing varies | | Git server | DBT development version control | > v2.33 | 1 server, or github/gitlab/etc. | | CI/CD server | DBT development | N/A | 1 server, or github/gitlab/etc. | | HTTPS Certificate | Security | N/A | Two certificates are needed; datacoves.yourdomain.com and *.datacoves.yourdomain.com. Certbot is supported. | | DNS Entries | Host Resolution | N/A | Exact configuration varies per cloud provider | __(*)__ min. requirements may vary depending on the number of environments. For smaller installations, only one database server is needed. ### Optional dependencies | Service | Purpose | Requirements | Quantity | |--------------|-----------------|--------------------------|------------| | Docker Registry | Docker images registry | Any docker API compliant image registry | 1 service account | | SMTP account | Airflow notifications | N/A | 1 service account | | Slack account | Airflow notifications | N/A | 1 account | | MS Teams account | Airflow notifications | N/A | 1 account | --- // File: reference/datacoves/rollback-strategy # Rollback Strategy Datacoves manages change across three distinct platform layers: infrastructure, platform services, and customer image. Each layer has different rollback considerations, summarized below. | Layer | Rollback Capability | Responsibility | |-------|-------------------|----------------| | Infrastructure | Cannot be undone once applied | Datacoves + Customer (collaborative upgrade, no rollback) | | Platform Services (Airflow, Python, etc.) | Cannot be rolled back | Datacoves Engineering | | Customer Image (dbt, packages, libraries) | Technically feasible but strongly discouraged | Customer-initiated, Datacoves-executed | ## Infrastructure Layer The infrastructure layer includes cloud provider services and container orchestration systems (such as Kubernetes) that host the Datacoves platform. Datacoves works collaboratively with the customer's team to plan and execute infrastructure upgrades. Both parties participate in testing prior to applying changes. :::warning Once an infrastructure upgrade is applied, it cannot be undone. Customers and Datacoves should ensure thorough testing before proceeding with any infrastructure-level change. ::: ## Platform Services Layer Platform-level services, such as Airflow versions, Python versions, and other core components managed by Datacoves, follow a forward-fix model. These versions cannot be rolled back once upgraded. If a defect or regression is identified following a platform release, the Datacoves engineering team will produce and deploy a **hotfix** as a new forward release that corrects the identified problem, ensuring the platform remains in a consistent, forward-progressing state. ### Defect Resolution Process 1. The customer reports the issue through the established support channel. 2. The Datacoves engineering team triages the issue and determines severity. 3. A hotfix is developed, tested, and deployed as a new forward release. 4. The customer is notified upon successful deployment and resolution. ## Customer Image Layer The customer image defines the specific versions of packages and libraries used in the customer's environment, including both standard components (such as dbt) and any additional tools. For example, if dbt is upgraded from version 1.10 to 1.11 and an issue is discovered, the image can be reverted to use dbt 1.10. ### Cascading Impact Risk :::warning While version rollback is technically feasible at this layer, **it is strongly discouraged** due to the risk of cascading disruptions in multi-user environments. ::: In enterprise environments where many users share the same Datacoves instance, rolling back a package version can produce unintended effects across the organization. Consider the following scenario: 1. A customer requests an upgrade to Package X (a third-party dependency). 2. After deployment, the new version introduces a breaking change that affects certain pipelines. 3. **User A** detects the issue early and modifies their code to accommodate the new version, restoring normal execution. 4. **User B**, also affected, requests a rollback of Package X to the prior version. 5. If the rollback is executed, **User A's code**, which was adapted to the newer version, now breaks under the restored older version. Additionally, rolling back a single package may introduce dependency conflicts. For example, if both a Snowflake connector library and dbt were upgraded together because they share a common dependency, rolling back only dbt could create an incompatibility between the two libraries. In an organization with dozens or hundreds of users on the platform concurrently, this type of cascading disruption can propagate rapidly and unpredictably. ### Recommended Approach Rather than reverting a shared package version, the recommended course of action is: 1. **Test before promoting to production.** Customers who maintain a separate testing cluster or environment can identify issues before they reach production users. 2. **Apply targeted fixes.** Affected users should update their own code to accommodate the new package version, preserving environmental consistency and avoiding new failures for users who have already adapted. A version rollback should be considered only as a **last resort**. ### Rollback Process (Last Resort) {#pis-rollback-process} If a rollback is determined to be necessary: 1. The customer identifies the package and the target version to revert to. 2. The customer submits a rollback request through the support channel. 3. Datacoves validates the request and confirms target version availability, including a review of potential dependency conflicts with other libraries in the image. 4. The image configuration is updated and deployed to the customer's environment. 5. The customer validates the rollback and confirms resolution. :::info If a rollback introduces new issues (for example, breaking other users' workflows or creating dependency conflicts), the customer assumes responsibility for resolving those downstream effects. ::: ## Summary of Responsibilities | Scenario | Datacoves | Customer | |----------|-----------|----------| | Infrastructure upgrade | Collaborate on planning and execution | Participate in testing before applying changes | | Infrastructure rollback | Cannot be undone | Cannot be undone | | Platform service defect after release | Develop and deploy a hotfix as a forward release | Report the issue through the support channel | | Customer image package version issue | Advise forward-fix approach; execute rollback only as a last resort with dependency review | Identify the target version, submit the request, and accept responsibility for downstream effects | --- // File: reference/security/README # Datacoves Security Security and Privacy are fundamental pillars in Datacoves. We are committed to keeping your data safe by following industry-leading standards for securing physical deployments, setting access policies, managing our network, and setting policies across our organization. ## Authentication Datacoves allows users to log in to the platform via Single Sign On(SSO) using your organization's Google or Microsoft account(contact support). ## Access Control Datacoves supports user management and role-based access control (RBAC). ## Communication & Encryption - Any HTTP connection attempt is forwarded to HTTPS. - We employ HSTS to guarantee that browsers only communicate with Datacoves over HTTPS. - All connections to Datacoves are by default encrypted, using contemporary ciphers and cryptographic techniques, in both directions. - For all data that is encrypted at rest, we use AES-128. ## Data Processing ### IDE The data from your database will traverse the Datacoves infrastructure on the way to your browser when you write interactive queries from the IDE. But this information is not preserved in any way (caching or otherwise). Outside of your browser sessions, it does not reside on our servers. ### Airbyte Service Airbyte connectors operate as the data pipes moving data from Point A to point B: Extracting data from data sources (APIs, files, databases) and loading it into destination platforms (warehouses, data lakes) with optional transformation performed at the data destination. As soon as data is transferred from the source to the destination, it is purged from your Datacoves environment. Environments with Airbyte installed store the following data: #### Technical Logs Technical logs are stored for troubleshooting purposes and may contain sensitive data based on the connection’s state data. If your connection is set to an Incremental sync mode, users choose which column is the cursor for their connection. While we strongly recommend a timestamp like an updated_at column, users can choose any column they want to be the cursor. #### Configuration Metadata Datacoves retains configuration details and data points such as table and column names for each integration. #### Sensitive Data​ As Datacoves is not aware of the data being transferred, users are required to follow the Terms of Service and are ultimately responsible for ensuring their data transfer is compliant with their jurisdiction. ## Data Storage Datacoves stores the following data persistently: - Datacoves account details, such as job definitions, database connection details, user information, etc. Raw data from your warehouse is not included in cloud account information. - Logs associated with jobs and interactive queries you’ve run. Unless the code you write creates it, the warehouse's data is not included in logs or assets. For instance, you could create code that reads every piece of customer information from your customer table and logs it. Although it's generally not a good idea to do that, it is conceivable and would imply that data is stored in Datacoves. ## Availability, Business Continuity, & Disaster Recovery Datacoves is hosted in Azure and Amazon Web Services, with availability in multiple AZ’s (availability zones) in a region. We save backups for at least seven (7) days. Our employees are dispersed remotely across the US and Latin America and we offer service to consumers everywhere. We can practically give help from anywhere thanks to our distributed staff, which also lessens the effects of support interruptions in certain geographic areas. ## Security Protocols Datacoves data centers are hosted using Azure and Amazon Web Services, where they are protected by electronic security, intrusion detection systems, and 24/7/365 human staff. Datacoves runs operating systems that are actively maintained, long-term supported, and patched with the most recent security updates. We only allow a few senior personnel access to sensitive information. Before deploying a platform release, we examine new features for any security risks. ## Security Recommendations Ensure that only the datasets processed by Airbyte, dbt, Airflow, or Superset are given access to your warehouse by Datacoves. To protect your data and login credentials while in transit, use SSL or SSH encryption. For users in your database, pick secure passwords or use key-based authentication when possible. ## Contact us To stay current with the most recent security methods, Datacoves is dedicated to collaborating with security professionals throughout the world. We kindly request that you notify us immediately if you discover any security flaws in Datacoves. Please email us at support@datacoves.com if you think you have found an issue or if you have any queries. --- // File: reference/vscode/datacoves-env-vars # Datacoves Environment Variables Datacoves streamlines your workflow by pre-setting environment variables to simplify work workflow such as the configuration needed to generate Airflow Dags with dbt-coves. You may also leverage these variables for your custom processes. These variables are created automatically and some may be adjusted via the admin settings. To view your set variables run: ``` bash env | grep DATACOVES | sort ``` ## Variables **DATACOVES__AIRBYTE_HOST_NAME**: Points to the Airbyte instance in the current environment. Set automatically by Datacoves. **DATACOVES__AIRBYTE_PORT**: Airbyte port. Set automatically by Datacoves. **DATACOVES__AIRFLOW_DAGS_PATH**: Path to folder Airflow will look for DAGs. Set environment settings > Service Configurations > Python DAGs path. **DATACOVES__AIRFLOW_DAGS_YML_PATH**: Path to folder dbt-coves will look for yaml files to generate python DAGs. **DATACOVES__AIRFLOW_DBT_PROFILE_PATH**: Path to the profiles.yml used by Airflow **DATACOVES__DATAHUB_HOST_NAME**: Host url for Datahub. **DATACOVES__DATAHUB_PORT**: Port for Datahub. **DATACOVES__DBT_HOME**: Relative path to the folder where the dbt_project.yml file is located. Set in your environment settings > Service Configurations > dbt project path. **DATACOVES__REPOSITORY_CLONE**: true or false. Will be true when git repository is properly configured and tested in your user settings. **DATACOVES__REPOSITORY_URL**: Repository associated with your project. Set in your user settings. **DATACOVES__USER_EMAIL**: Email associated with your account. --- // File: reference/vscode/README # Datacoves reference - vscode